﻿
#define SPADAS_DEPRECATED_HIDE
#include "NextYUVConversion.h"
#include "ffmpegdl.h"

namespace videonext
{
	Array<UInt> tableYuv2Bgr;
	Lock tableYuv2BgrLock;

    struct StructImage
    {
        Int width;
        Int height;
        Int widthStep;
        void *imageData;

		StructImage() : width(0), height(0), widthStep(0), imageData(NULL)
		{}
    };

	Array<UInt> getTableYuv2Bgr()
	{
		const UInt COARSE_SCALE = 128;
		// const UInt COARSE_SCALE = 64;

		LockProxy lp(tableYuv2BgrLock);
		if (tableYuv2Bgr.isEmpty())
		{
			tableYuv2Bgr = Array<UInt>(COARSE_SCALE * COARSE_SCALE * COARSE_SCALE);
			UInt *data = tableYuv2Bgr.data();
			for (UInt yIndex = 0; yIndex < COARSE_SCALE; yIndex++)
			{
				Int yVal = 2 * yIndex; // COARSE_SCALE=128
				// Int yVal = 4 * yIndex + 2; // COARSE_SCALE=64
				for (UInt uIndex = 0; uIndex < COARSE_SCALE; uIndex++)
				{
					Int uVal = 2 * uIndex; // COARSE_SCALE=128
					// Int uVal = 4 * uIndex + 2; // COARSE_SCALE=64
					for (UInt vIndex = 0; vIndex < COARSE_SCALE; vIndex++)
					{
						Int vVal = 2 * vIndex; // COARSE_SCALE=128
						UInt index = yIndex | (uIndex << 7) | (vIndex << 14); // COARSE_SCALE=128
						// Int vVal = 4 * vIndex + 2; // COARSE_SCALE=64
						// UInt index = yIndex | (uIndex << 6) | (vIndex << 12); // COARSE_SCALE=64
						
						Byte bVal = (Byte)(math::clamp(yVal * 65536 + uVal * 116129 - 14864512, 0, 16711680) >> 16);
						Byte gVal = (Byte)(math::clamp(yVal * 65536 - uVal * 22553 - vVal * 46801 + 8877312, 0, 16711680) >> 16);
						Byte rVal = (Byte)(math::clamp(yVal * 65536 + vVal * 91881 - 11760768, 0, 16711680) >> 16);

						data[index] = (UInt)bVal | ((UInt)gVal << 8) | ((UInt)rVal << 16);
					}
				}
			}
		}
		return tableYuv2Bgr;
	}

    Bool convStructImage(CompatibleChannelData channel, StructImage& img)
    {
        if (!channel.data()) return FALSE;

        img.width = channel.width() * (channel.bbp() == 2 ? 2 : 1);
        img.height = channel.height();
        img.imageData = channel.data();
        img.widthStep = channel.step();

        return TRUE;
    }

	void byteGrayHalf(StructImage *src, StructImage *dst, Int srcOffsetU, Int srcOffsetV)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dstData = (Byte*)dst->imageData;

		for (Int vSrc = srcOffsetV, vDst = 0; vSrc + 1 < src->height && vDst < dst->height; vSrc += 2, vDst++)
		{
			Byte *srcRow1 = &srcData[vSrc * src->widthStep];
			Byte *srcRow2 = &srcData[(vSrc + 1) * src->widthStep];
			Byte *dstRow = &dstData[vDst * dst->widthStep];
			for (Int uSrc = srcOffsetU, uDst = 0; uSrc + 1 < src->width && uDst < dst->width; uSrc += 2, uDst++)
			{
				dstRow[uDst] = (Byte)(((UInt)srcRow1[uSrc] + (UInt)srcRow1[uSrc + 1] + (UInt)srcRow2[uSrc] + (UInt)srcRow2[uSrc + 1]) >> 2);
			}
		}
	}

	void byteGrayThird(StructImage *src, StructImage *dst, Int srcOffsetU, Int srcOffsetV)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dstData = (Byte*)dst->imageData;

		for (Int vSrc = srcOffsetV, vDst = 0; vSrc + 2 < src->height && vDst < dst->height; vSrc += 3, vDst++)
		{
			Byte *srcRow1 = &srcData[vSrc * src->widthStep];
			Byte *srcRow2 = &srcData[(vSrc + 1) * src->widthStep];
			Byte *srcRow3 = &srcData[(vSrc + 2) * src->widthStep];
			Byte *dstRow = &dstData[vDst * dst->widthStep];
			for (Int uSrc = srcOffsetU, uDst = 0; uSrc + 2 < src->width && uDst < dst->width; uSrc += 3, uDst++)
			{
				dstRow[uDst] = (Byte)(((UInt)srcRow1[uSrc] + (UInt)srcRow1[uSrc + 1] + (UInt)srcRow1[uSrc + 2] +
					(UInt)srcRow2[uSrc] + (UInt)srcRow2[uSrc + 2] +
					(UInt)srcRow3[uSrc] + (UInt)srcRow3[uSrc + 1] + (UInt)srcRow3[uSrc + 2]) >> 3);
			}
		}
	}

	void byteGrayDoubleThird(StructImage *src, StructImage *dst, Int srcOffsetU, Int srcOffsetV)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dstData = (Byte*)dst->imageData;

		for (Int vSrc = srcOffsetV, vDst = 0; vSrc + 2 < src->height && vDst + 1 < dst->height; vSrc += 3, vDst += 2)
		{
			Byte *srcRow1 = &srcData[vSrc * src->widthStep];
			Byte *srcRow2 = &srcData[(vSrc + 2) * src->widthStep];
			Byte *dstRow1 = &dstData[vDst * dst->widthStep];
			Byte *dstRow2 = &dstData[(vDst + 1) * dst->widthStep];
			for (Int uSrc = srcOffsetU, uDst = 0; uSrc + 2 < src->width && uDst + 1 < dst->width; uSrc += 3, uDst += 2)
			{
				dstRow1[uDst] = srcRow1[uSrc];
				dstRow1[uDst + 1] = srcRow1[uSrc + 2];
				dstRow2[uDst] = srcRow2[uSrc];
				dstRow2[uDst + 1] = srcRow2[uSrc + 2];
			}
		}
	}

	void byteGrayQuarter(StructImage *src, StructImage *dst, Int srcOffsetU, Int srcOffsetV)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dstData = (Byte*)dst->imageData;

		for (Int vSrc = srcOffsetV, vDst = 0; vSrc + 3 < src->height && vDst < dst->height; vSrc += 4, vDst++)
		{
			Byte *srcRow1 = &srcData[vSrc * src->widthStep];
			Byte *srcRow2 = &srcData[(vSrc + 1) * src->widthStep];
			Byte *srcRow3 = &srcData[(vSrc + 2) * src->widthStep];
			Byte *srcRow4 = &srcData[(vSrc + 3) * src->widthStep];
			Byte *dstRow = &dstData[vDst * dst->widthStep];
			for (Int uSrc = srcOffsetU, uDst = 0; uSrc + 3 < src->width && uDst < dst->width; uSrc += 4, uDst++)
			{
				dstRow[uDst] = (Byte)(((UInt)srcRow1[uSrc] + (UInt)srcRow1[uSrc + 1] + (UInt)srcRow1[uSrc + 2] + (UInt)srcRow1[uSrc + 3] +
					(UInt)srcRow2[uSrc] + (UInt)srcRow2[uSrc + 1] + (UInt)srcRow2[uSrc + 2] + (UInt)srcRow2[uSrc + 3] +
					(UInt)srcRow3[uSrc] + (UInt)srcRow3[uSrc + 1] + (UInt)srcRow3[uSrc + 2] + (UInt)srcRow3[uSrc + 3] +
					(UInt)srcRow4[uSrc] + (UInt)srcRow4[uSrc + 1] + (UInt)srcRow4[uSrc + 2] + (UInt)srcRow4[uSrc + 3]) >> 4);
			}
		}
	}

	void byteGraySixth(StructImage *src, StructImage *dst, Int srcOffsetU, Int srcOffsetV)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dstData = (Byte*)dst->imageData;

		for (Int vSrc = srcOffsetV, vDst = 0; vSrc + 5 < src->height && vDst < dst->height; vSrc += 6, vDst++)
		{
			Byte *srcRow1 = &srcData[vSrc * src->widthStep];
			Byte *srcRow2 = &srcData[(vSrc + 1) * src->widthStep];
			Byte *srcRow3 = &srcData[(vSrc + 2) * src->widthStep];
			Byte *srcRow4 = &srcData[(vSrc + 3) * src->widthStep];
			Byte *srcRow5 = &srcData[(vSrc + 4) * src->widthStep];
			Byte *srcRow6 = &srcData[(vSrc + 5) * src->widthStep];
			Byte *dstRow = &dstData[vDst * dst->widthStep];
			for (Int uSrc = srcOffsetU, uDst = 0; uSrc + 5 < src->width && uDst < dst->width; uSrc += 6, uDst++)
			{
				dstRow[uDst] = (Byte)(((UInt)srcRow1[uSrc] + (UInt)srcRow1[uSrc + 1] + (UInt)srcRow1[uSrc + 2] + (UInt)srcRow1[uSrc + 3] + (UInt)srcRow1[uSrc + 4] + (UInt)srcRow1[uSrc + 5] +
					(UInt)srcRow2[uSrc] + (UInt)srcRow2[uSrc + 2] + (UInt)srcRow2[uSrc + 3] + (UInt)srcRow2[uSrc + 5] +
					(UInt)srcRow3[uSrc] + (UInt)srcRow3[uSrc + 1] + (UInt)srcRow3[uSrc + 2] + (UInt)srcRow3[uSrc + 3] + (UInt)srcRow3[uSrc + 4] + (UInt)srcRow3[uSrc + 5] +
					(UInt)srcRow4[uSrc] + (UInt)srcRow4[uSrc + 1] + (UInt)srcRow4[uSrc + 2] + (UInt)srcRow4[uSrc + 3] + (UInt)srcRow4[uSrc + 4] + (UInt)srcRow4[uSrc + 5] +
					(UInt)srcRow5[uSrc] + (UInt)srcRow5[uSrc + 2] + (UInt)srcRow5[uSrc + 3] + (UInt)srcRow5[uSrc + 5] +
					(UInt)srcRow6[uSrc] + (UInt)srcRow6[uSrc + 1] + (UInt)srcRow6[uSrc + 2] + (UInt)srcRow6[uSrc + 3] + (UInt)srcRow6[uSrc + 4] + (UInt)srcRow6[uSrc + 5]) >> 5);
			}
		}
	}

	void byteGrayDouble(StructImage *src, StructImage *dst, Int srcOffsetU, Int srcOffsetV)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dstData = (Byte*)dst->imageData;
		for (Int vSrc = srcOffsetV, vDst = 0; vSrc < src->height && vDst + 1 < dst->height; vSrc++, vDst += 2)
		{
			Byte *srcRow = (Byte*)&srcData[vSrc * src->widthStep];
			Byte *dstRow1 = (Byte*)&dstData[vDst * dst->widthStep];
			Byte *dstRow2 = (Byte*)&dstData[(vDst + 1) * dst->widthStep];
			for (Int uSrc = srcOffsetU, uDst = 0; uSrc < src->width && uDst + 1 < dst->width; uSrc++, uDst += 2)
			{
				Byte val = srcRow[uSrc];
				dstRow1[uDst] = val;
				dstRow1[uDst + 1] = val;
				dstRow2[uDst] = val;
				dstRow2[uDst + 1] = val;
			}
		}
	}

	void byteGrayCopy(StructImage *src, StructImage *dst, Int srcOffsetU, Int srcOffsetV)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dstData = (Byte*)dst->imageData;

		for (Int vSrc = srcOffsetV, vDst = 0; vSrc < src->height && vDst < dst->height; vSrc++, vDst++)
		{
			Byte *srcRow = &srcData[vSrc * src->widthStep];
			Byte *dstRow = &dstData[vDst * dst->widthStep];
			Int srcCopyWidth = src->width - srcOffsetU;
			if (srcCopyWidth > 0) utility::memoryCopy(&srcRow[srcOffsetU], dstRow, math::min(srcCopyWidth, dst->width));
		}
	}

	void nv12Copy(StructImage *src, StructImage *dst1, StructImage *dst2)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dst1Data = (Byte*)dst1->imageData;
		Byte *dst2Data = (Byte*)dst2->imageData;

		for (Int v = 0; v < src->height && v < dst1->height; v++)
		{
			Byte *srcRow = &srcData[v * src->widthStep];
			Byte *dst1Row = &dst1Data[v * dst1->widthStep];
			Byte *dst2Row = &dst2Data[v * dst2->widthStep];
			for (Int uSrc = 0, uDst = 0; uSrc + 1 < src->width && uDst < dst1->width; uSrc += 2, uDst++)
			{
				dst1Row[uDst] = srcRow[uSrc];
				dst2Row[uDst] = srcRow[uSrc + 1];
			}
		}
	}

	void nv12Half(StructImage *src, StructImage *dst1, StructImage *dst2)
	{
		Byte *srcData = (Byte*)src->imageData;
		Byte *dst1Data = (Byte*)dst1->imageData;
		Byte *dst2Data = (Byte*)dst2->imageData;

		for (Int vSrc = 0, vDst = 0; vSrc + 1 < src->height && vDst < dst1->height; vSrc += 2, vDst++)
		{
			Byte *srcRow1 = &srcData[vSrc * src->widthStep];
			Byte *srcRow2 = &srcData[(vSrc + 1) * src->widthStep];
			Byte *dst1Row = &dst1Data[vDst * dst1->widthStep];
			Byte *dst2Row = &dst2Data[vDst * dst2->widthStep];
			for (Int uSrc = 0, uDst = 0; uSrc + 3 < src->width && uDst < dst1->width; uSrc += 4, uDst++)
			{
				dst1Row[uDst] = (Byte)(((UInt)srcRow1[uSrc] + (UInt)srcRow1[uSrc + 2] + (UInt)srcRow2[uSrc] + (UInt)srcRow2[uSrc + 2]) >> 2);
				dst2Row[uDst] = (Byte)(((UInt)srcRow1[uSrc + 1] + (UInt)srcRow1[uSrc + 3] + (UInt)srcRow2[uSrc + 1] + (UInt)srcRow2[uSrc + 3]) >> 2);
			}
		}
	}

	void yuv411ToYuv420(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		UInt srcStep = input.size() / inputSize.height;
		Int halfHeight = (Int)inputSize.height / 2;
		Int blocks = (Int)inputSize.width / 16; // 每block含16像素和24字节(6xUInt)

		for (Int v = 0; v < halfHeight; v++)
		{
			UInt *srcRow1 = (UInt*)&srcData[(inputSize.height - 2 * v - 1) * srcStep];
			UInt *srcRow2 = (UInt*)&srcData[(inputSize.height - 2 * v - 2) * srcStep];
			UInt *yRow1 = (UInt*)&yData[2 * v * yImage->widthStep];
			UInt *yRow2 = (UInt*)&yData[(2 * v + 1) * yImage->widthStep];
			UInt *uRow = (UInt*)&uData[v * uImage->widthStep];
			UInt *vRow = (UInt*)&vData[v * vImage->widthStep];

			UInt srcYI = 0, dstYI = 0, dstUVI = 0;
			for (Int b = 0; b < blocks; b++)
			{
				UInt b10 = srcRow1[srcYI];
				UInt b20 = srcRow2[srcYI++];
				UInt b11 = srcRow1[srcYI];
				UInt b21 = srcRow2[srcYI++];
				UInt b12 = srcRow1[srcYI];
				UInt b22 = srcRow2[srcYI++];
				UInt b13 = srcRow1[srcYI];
				UInt b23 = srcRow2[srcYI++];
				UInt b14 = srcRow1[srcYI];
				UInt b24 = srcRow2[srcYI++];
				UInt b15 = srcRow1[srcYI];
				UInt b25 = srcRow2[srcYI++];

				UInt valU0 = ((b10 & 0x000000ff) + (b20 & 0x000000ff)) >> 1;
				UInt valU1 = ((b11 & 0x000000ff) + (b21 & 0x000000ff)) >> 1;
				UInt valU2 = ((b13 & 0x000000ff) + (b23 & 0x000000ff)) >> 1;
				UInt valU3 = ((b14 & 0x000000ff) + (b24 & 0x000000ff)) >> 1;
				UInt valV0 = ((b10 & 0x00ff0000) + (b20 & 0x00ff0000)) >> 17;
				UInt valV1 = ((b11 & 0x00ff0000) + (b21 & 0x00ff0000)) >> 17;
				UInt valV2 = ((b13 & 0x00ff0000) + (b23 & 0x00ff0000)) >> 17;
				UInt valV3 = ((b14 & 0x00ff0000) + (b24 & 0x00ff0000)) >> 17;

				uRow[dstUVI] = valU0 | (valU0 << 8) | (valU1 << 16) | (valU1 << 24);
				vRow[dstUVI++] = valV0 | (valV0 << 8) | (valV1 << 16) | (valV1 << 24);
				uRow[dstUVI] = valU2 | (valU2 << 8) | (valU3 << 16) | (valU3 << 24);
				vRow[dstUVI++] = valV2 | (valV2 << 8) | (valV3 << 16) | (valV3 << 24);

				yRow1[dstYI] = ((b10 & 0x0000ff00) >> 8) | ((b10 & 0xff000000) >> 16) | ((b11 & 0x0000ff00) << 8) | (b11 & 0xff000000);
				yRow2[dstYI++] = ((b20 & 0x0000ff00) >> 8) | ((b20 & 0xff000000) >> 16) | ((b21 & 0x0000ff00) << 8) | (b21 & 0xff000000);
				yRow1[dstYI] = b12;
				yRow2[dstYI++] = b22;
				yRow1[dstYI] = ((b13 & 0x0000ff00) >> 8) | ((b13 & 0xff000000) >> 16) | ((b14 & 0x0000ff00) << 8) | (b14 & 0xff000000);
				yRow2[dstYI++] = ((b23 & 0x0000ff00) >> 8) | ((b23 & 0xff000000) >> 16) | ((b24 & 0x0000ff00) << 8) | (b24 & 0xff000000);
				yRow1[dstYI] = b15;
				yRow2[dstYI++] = b25;
			}
		}
	}

	void yuv4pToYuv420(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		Int halfHeight = (Int)inputSize.height / 2;
		Int halfWidth = (Int)inputSize.width / 2;
		UInt srcStep = input.size() / halfHeight;

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *srcRow = (Byte*)&srcData[v * srcStep];
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			Byte *uRow = (Byte*)&uData[v * uImage->widthStep];
			Byte *vRow = (Byte*)&vData[v * vImage->widthStep];

			UInt srcI = 0, dstY1I = 0, dstY2I = 0, dstUI = 0, dstVI = 0;
			for (Int u = 0; u < halfWidth; u++)
			{
				uRow[dstUI++] = srcRow[srcI++] ^ 0x80;
				vRow[dstVI++] = srcRow[srcI++] ^ 0x80;
				yRow1[dstY1I++] = srcRow[srcI++];
				yRow1[dstY1I++] = srcRow[srcI++];
				yRow2[dstY2I++] = srcRow[srcI++];
				yRow2[dstY2I++] = srcRow[srcI++];
			}
		}
	}

	void yuv422ToYuv420(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		Int halfHeight = (Int)inputSize.height / 2;
		Int halfWidth = (Int)inputSize.width / 2;
		UInt srcStep = input.size() / inputSize.height;

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *srcRow1 = (Byte*)&srcData[2 * v * srcStep];
			Byte *srcRow2 = (Byte*)&srcData[(2 * v + 1) * srcStep];
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			Byte *uRow = (Byte*)&uData[v * uImage->widthStep];
			Byte *vRow = (Byte*)&vData[v * vImage->widthStep];

			UInt src1I = 0, src2I = 0, dstY1I = 0, dstY2I = 0, dstUI = 0, dstVI = 0;
			for (Int u = 0; u < halfWidth; u++)
			{
				yRow1[dstY1I++] = srcRow1[src1I++];
				yRow2[dstY2I++] = srcRow2[src2I++];
				uRow[dstUI++] = (Byte)(((UInt)srcRow1[src1I++] + (UInt)srcRow2[src2I++]) >> 1);
				yRow1[dstY1I++] = srcRow1[src1I++];
				yRow2[dstY2I++] = srcRow2[src2I++];
				vRow[dstVI++] = (Byte)(((UInt)srcRow1[src1I++] + (UInt)srcRow2[src2I++]) >> 1);
			}
		}
	}

	void raw8ToYuv420(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		Int width = (Int)inputSize.width;
		Int halfHeight = (Int)inputSize.height / 2;
		UInt srcStep = input.size() / inputSize.height;

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *srcRow1 = (Byte*)&srcData[2 * v * srcStep];
			Byte *srcRow2 = (Byte*)&srcData[(2 * v + 1) * srcStep];
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			Byte *uRow = (Byte*)&uData[v * uImage->widthStep];
			Byte *vRow = (Byte*)&vData[v * vImage->widthStep];

			UInt dstY = 1, dstUV = 0;
			Int val11 = (Int)srcRow1[0], val12 = (Int)srcRow1[1], val21 = (Int)srcRow2[0], val22 = (Int)srcRow2[1];
			Bool writeUV = TRUE;
			for (Int u = 2; u < width; u++)
			{
				Int val13 = (Int)srcRow1[u];
				Int val23 = (Int)srcRow2[u];
				if (writeUV)
				{
					Int blue2 = val11 + val13;
					Int green2 = val21 + val23;
					Int greenCross = val12 + val21;
					Int brSum = val22 * 19589 + blue2 * 3751;
					yRow1[dstY] = (Byte)((brSum + val12 * 38443) >> 16);
					yRow2[dstY++] = (Byte)((brSum + green2 * 19221) >> 16);
					uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
					vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
				}
				else
				{
					Int red2 = val21 + val23;
					Int green2 = val11 + val13;
					Int brSum = val12 * 7502 + red2 * 9794;
					yRow1[dstY] = (Byte)((brSum + green2 * 19221) >> 16);
					yRow2[dstY++] = (Byte)((brSum + val22 * 38443) >> 16);
				}
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;
				writeUV = !writeUV;
			}
			if ((width & 0x01) == 0)
			{
				Int greenCross = val12 + val21;
				uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
				vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
			}
		}

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			yRow1[0] = 0;
			yRow2[0] = 0;
			yRow1[yImage->widthStep - 1] = 0;
			yRow2[yImage->widthStep - 1] = 0;
		}
	}

	void raw12ToYuv420(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		Int halfHeight = (Int)inputSize.height / 2;
		Int halfWidth = (Int)inputSize.width / 2;
		UInt srcStep = input.size() / inputSize.height;

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *srcRow1 = (Byte*)&srcData[2 * v * srcStep];
			Byte *srcRow2 = (Byte*)&srcData[(2 * v + 1) * srcStep];
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			Byte *uRow = (Byte*)&uData[v * uImage->widthStep];
			Byte *vRow = (Byte*)&vData[v * vImage->widthStep];

			UInt src1I = 0, src2I = 0;
			Byte b11 = srcRow1[src1I++];
			Byte b12 = srcRow1[src1I++];
			Byte b13 = srcRow1[src1I++];
			Byte b21 = srcRow2[src2I++];
			Byte b22 = srcRow2[src2I++];
			Byte b23 = srcRow2[src2I++];

			UInt dstY = 1, dstUV = 0;
			Int val11 = (Int)(((b11 & 0xf0) >> 4) | ((b12 & 0x0f) << 4));
			Int val12 = (Int)b13;
			Int val21 = (Int)(((b21 & 0xf0) >> 4) | ((b22 & 0x0f) << 4));
			Int val22 = (Int)b23;
			for (Int u = 1; u < halfWidth; u++)
			{
				b11 = srcRow1[src1I++];
				b12 = srcRow1[src1I++];
				b13 = srcRow1[src1I++];
				b21 = srcRow2[src2I++];
				b22 = srcRow2[src2I++];
				b23 = srcRow2[src2I++];

				Int val13 = (Int)(((b11 & 0xf0) >> 4) | ((b12 & 0x0f) << 4));
				Int val23 = (Int)(((b21 & 0xf0) >> 4) | ((b22 & 0x0f) << 4));
				Int blue2 = val11 + val13;
				Int green2 = val21 + val23;
				Int greenCross = val12 + val21;
				Int brSum = val22 * 19589 + blue2 * 3751;
				yRow1[dstY] = (Byte)((brSum + val12 * 38443) >> 16);
				yRow2[dstY++] = (Byte)((brSum + green2 * 19221) >> 16);
				uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
				vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;

				val13 = (Int)b13;
				val23 = (Int)b23;
				Int red2 = val21 + val23;
				green2 = val11 + val13;
				brSum = val12 * 7502 + red2 * 9794;
				yRow1[dstY] = (Byte)((brSum + green2 * 19221) >> 16);
				yRow2[dstY++] = (Byte)((brSum + val22 * 38443) >> 16);
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;
			}
			if ((inputSize.width & 0x01) == 0)
			{
				Int greenCross = val12 + val21;
				uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
				vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
			}
		}

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			yRow1[0] = 0;
			yRow2[0] = 0;
			yRow1[yImage->widthStep - 1] = 0;
			yRow2[yImage->widthStep - 1] = 0;
		}
	}

	void raw14ToYuv420(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		Int halfHeight = (Int)inputSize.height / 2;
		Int oneFourWidth = (Int)inputSize.width / 4;
		UInt srcStep = input.size() / inputSize.height;

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *srcRow1 = (Byte*)&srcData[2 * v * srcStep];
			Byte *srcRow2 = (Byte*)&srcData[(2 * v + 1) * srcStep];
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			Byte *uRow = (Byte*)&uData[v * uImage->widthStep];
			Byte *vRow = (Byte*)&vData[v * vImage->widthStep];

			UInt src1I = 0, src2I = 0;
			Byte b11 = srcRow1[src1I++];
			Byte b12 = srcRow1[src1I++];
			Byte b13 = srcRow1[src1I++];
			Byte b14 = srcRow1[src1I++];
			Byte b15 = srcRow1[src1I++];
			Byte b16 = srcRow1[src1I++];
			Byte b17 = srcRow1[src1I++];
			Byte b21 = srcRow2[src2I++];
			Byte b22 = srcRow2[src2I++];
			Byte b23 = srcRow2[src2I++];
			Byte b24 = srcRow2[src2I++];
			Byte b25 = srcRow2[src2I++];
			Byte b26 = srcRow2[src2I++];
			Byte b27 = srcRow2[src2I++];

			UInt dstY = 1, dstUV = 0;
			Int val11 = (Int)(((b11 & 0xc0) >> 6) | ((b12 & 0x3f) << 2));
			Int val12 = (Int)(((b13 & 0xf0) >> 4) | ((b14 & 0x0f) << 4));
			Int val21 = (Int)(((b21 & 0xc0) >> 6) | ((b22 & 0x3f) << 2));
			Int val22 = (Int)(((b23 & 0xf0) >> 4) | ((b24 & 0x0f) << 4));

			Int val13 = (Int)(((b15 & 0xfc) >> 2) | ((b16 & 0x03) << 6));
			Int val23 = (Int)(((b25 & 0xfc) >> 2) | ((b26 & 0x03) << 6));
			Int blue2 = val11 + val13;
			Int green2 = val21 + val23;
			Int greenCross = val12 + val21;
			Int brSum = val22 * 19589 + blue2 * 3751;
			yRow1[dstY] = (Byte)((brSum + val12 * 38443) >> 16);
			yRow2[dstY++] = (Byte)((brSum + green2 * 19221) >> 16);
			uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
			vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
			val11 = val12;
			val12 = val13;
			val21 = val22;
			val22 = val23;

			val13 = (Int)b17;
			val23 = (Int)b27;
			Int red2 = val21 + val23;
			green2 = val11 + val13;
			brSum = val12 * 7502 + red2 * 9794;
			yRow1[dstY] = (Byte)((brSum + green2 * 19221) >> 16);
			yRow2[dstY++] = (Byte)((brSum + val22 * 38443) >> 16);
			val11 = val12;
			val12 = val13;
			val21 = val22;
			val22 = val23;

			for (Int u = 1; u < oneFourWidth; u++)
			{
				b11 = srcRow1[src1I++];
				b12 = srcRow1[src1I++];
				b13 = srcRow1[src1I++];
				b14 = srcRow1[src1I++];
				b15 = srcRow1[src1I++];
				b16 = srcRow1[src1I++];
				b17 = srcRow1[src1I++];
				b21 = srcRow2[src2I++];
				b22 = srcRow2[src2I++];
				b23 = srcRow2[src2I++];
				b24 = srcRow2[src2I++];
				b25 = srcRow2[src2I++];
				b26 = srcRow2[src2I++];
				b27 = srcRow2[src2I++];

				val13 = (Int)(((b11 & 0xc0) >> 6) | ((b12 & 0x3f) << 2));
				val23 = (Int)(((b21 & 0xc0) >> 6) | ((b22 & 0x3f) << 2));
				blue2 = val11 + val13;
				green2 = val21 + val23;
				greenCross = val12 + val21;
				brSum = val22 * 19589 + blue2 * 3751;
				yRow1[dstY] = (Byte)((brSum + val12 * 38443) >> 16);
				yRow2[dstY++] = (Byte)((brSum + green2 * 19221) >> 16);
				uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
				vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;

				val13 = (Int)(((b13 & 0xf0) >> 4) | ((b14 & 0x0f) << 4));
				val23 = (Int)(((b23 & 0xf0) >> 4) | ((b24 & 0x0f) << 4));
				red2 = val21 + val23;
				green2 = val11 + val13;
				brSum = val12 * 7502 + red2 * 9794;
				yRow1[dstY] = (Byte)((brSum + green2 * 19221) >> 16);
				yRow2[dstY++] = (Byte)((brSum + val22 * 38443) >> 16);
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;

				val13 = (Int)(((b15 & 0xfc) >> 2) | ((b16 & 0x03) << 6));
				val23 = (Int)(((b25 & 0xfc) >> 2) | ((b26 & 0x03) << 6));
				blue2 = val11 + val13;
				green2 = val21 + val23;
				greenCross = val12 + val21;
				brSum = val22 * 19589 + blue2 * 3751;
				yRow1[dstY] = (Byte)((brSum + val12 * 38443) >> 16);
				yRow2[dstY++] = (Byte)((brSum + green2 * 19221) >> 16);
				uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
				vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;

				val13 = (Int)b17;
				val23 = (Int)b27;
				red2 = val21 + val23;
				green2 = val11 + val13;
				brSum = val12 * 7502 + red2 * 9794;
				yRow1[dstY] = (Byte)((brSum + green2 * 19221) >> 16);
				yRow2[dstY++] = (Byte)((brSum + val22 * 38443) >> 16);
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;
			}
			if ((inputSize.width & 0x01) == 0)
			{
				Int greenCross = val12 + val21;
				uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
				vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
			}
		}

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			yRow1[0] = 0;
			yRow2[0] = 0;
			yRow1[yImage->widthStep - 1] = 0;
			yRow2[yImage->widthStep - 1] = 0;
		}
	}

	void raw16ToYuv420(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		Int halfHeight = (Int)inputSize.height / 2;
		Int width = (Int)inputSize.width;
		UInt srcStep = input.size() / inputSize.height;

		for (Int v = 0; v < halfHeight; v++)
		{
			Word *srcRow1 = (Word*)&srcData[2 * v * srcStep];
			Word *srcRow2 = (Word*)&srcData[(2 * v + 1) * srcStep];
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			Byte *uRow = (Byte*)&uData[v * uImage->widthStep];
			Byte *vRow = (Byte*)&vData[v * vImage->widthStep];

			UInt dstY = 1, dstUV = 0;
			Int val11 = (Int)(srcRow1[0] & 0xff), val12 = (Int)(srcRow1[1] & 0xff), val21 = (Int)(srcRow2[0] & 0xff), val22 = (Int)(srcRow2[1] & 0xff);
			Bool writeUV = TRUE;
			for (Int u = 2; u < width; u++)
			{
				Int val13 = (Int)(srcRow1[u] & 0xff);
				Int val23 = (Int)(srcRow2[u] & 0xff);
				if (writeUV)
				{
					Int blue2 = val11 + val13;
					Int green2 = val21 + val23;
					Int greenCross = val12 + val21;
					Int brSum = val22 * 19589 + blue2 * 3751;
					yRow1[dstY] = (Byte)((brSum + val12 * 38443) >> 16);
					yRow2[dstY++] = (Byte)((brSum + green2 * 19221) >> 16);
					uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
					vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
				}
				else
				{
					Int red2 = val21 + val23;
					Int green2 = val11 + val13;
					Int brSum = val12 * 7502 + red2 * 9794;
					yRow1[dstY] = (Byte)((brSum + green2 * 19221) >> 16);
					yRow2[dstY++] = (Byte)((brSum + val22 * 38443) >> 16);
				}
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;
				writeUV = !writeUV;
			}
			if ((width & 0x01) == 0)
			{
				Int greenCross = val12 + val21;
				uRow[dstUV] = (Byte)((val22 * (-11058) + greenCross * (-10854) + (val11 << 15) + 8388608) >> 16);
				vRow[dstUV++] = (Byte)((val11 * (-5328) + greenCross * (-13719) + (val22 << 15) + 8388608) >> 16);
			}
		}

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *yRow1 = (Byte*)&yData[2 * v * yImage->widthStep];
			Byte *yRow2 = (Byte*)&yData[(2 * v + 1) * yImage->widthStep];
			yRow1[0] = 0;
			yRow2[0] = 0;
			yRow1[yImage->widthStep - 1] = 0;
			yRow2[yImage->widthStep - 1] = 0;
		}
	}

	void y16ToYuv420(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		Int halfHeight = (Int)inputSize.height / 2;
		Int halfWidth = (Int)inputSize.width / 2;
		UInt srcStep = input.size() / inputSize.height;

		for (UInt v = 0; v < inputSize.height; v++)
		{
			Word *srcRow = (Word*)&srcData[v * srcStep];
			Byte *yRow = &yData[v * yImage->widthStep];

			for (UInt u = 0; u < inputSize.width; u++)
			{
				yRow[u] = (Byte)(srcRow[u] & 0xff);
			}
		}

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *uRow = (Byte*)&uData[v * uImage->widthStep];
			utility::memorySet(128, uRow, halfWidth);

			Byte *vRow = (Byte*)&vData[v * vImage->widthStep];
			utility::memorySet(128, vRow, halfWidth);
		}
	}

	void yuv411Copy(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		UInt srcStep = input.size() / inputSize.height;
		Int blocks = (Int)inputSize.width / 16;

		for (Int v = 0; v < (Int)inputSize.height; v++)
		{
			UInt *srcRow = (UInt*)&srcData[(inputSize.height - v - 1) * srcStep];
			UInt *yRow = (UInt*)&yData[v * yImage->widthStep];
			UInt *uRow = (UInt*)&uData[v * uImage->widthStep];
			UInt *vRow = (UInt*)&vData[v * vImage->widthStep];

			UInt srcI = 0, dstI = 0;
			for (Int b = 0; b < blocks; b++)
			{
				UInt b0 = srcRow[srcI++];
				UInt b1 = srcRow[srcI++];
				UInt b2 = srcRow[srcI++];
				UInt b3 = srcRow[srcI++];
				UInt b4 = srcRow[srcI++];
				UInt b5 = srcRow[srcI++];

				uRow[b] = (b0 & 0x000000ff) | ((b1 & 0x000000ff) << 8) | ((b3 & 0x000000ff) << 16) | ((b4 & 0x000000ff) << 24);
				vRow[b] = ((b0 & 0x00ff0000) >> 16) | ((b1 & 0x00ff0000) >> 8) | (b3 & 0x00ff0000) | ((b4 & 0x00ff0000) << 8);
				yRow[dstI++] = ((b0 & 0x0000ff00) >> 8) | ((b0 & 0xff000000) >> 16) | ((b1 & 0x0000ff00) << 8) | (b1 & 0xff000000);
				yRow[dstI++] = b2;
				yRow[dstI++] = ((b3 & 0x0000ff00) >> 8) | ((b3 & 0xff000000) >> 16) | ((b4 & 0x0000ff00) << 8) | (b4 & 0xff000000);
				yRow[dstI++] = b5;
			}
		}
	}

	void yuv411CopySameSize(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		UInt srcStep = input.size() / inputSize.height;
		Int blocks = (Int)inputSize.width / 8;

		for (Int v = 0; v < (Int)inputSize.height; v++)
		{
			Byte *srcRow = &srcData[(inputSize.height - v - 1) * srcStep];
			Byte *yRow = &yData[v * yImage->widthStep];
			Byte *uRow = &uData[v * uImage->widthStep];
			Byte *vRow = &vData[v * vImage->widthStep];

			for (Int b = 0; b < blocks; b++)
			{
				uRow[8 * b] = srcRow[12 * b];
				uRow[8 * b + 1] = srcRow[12 * b];
				uRow[8 * b + 2] = srcRow[12 * b];
				uRow[8 * b + 3] = srcRow[12 * b];
				yRow[8 * b] = srcRow[12 * b + 1];
				vRow[8 * b] = srcRow[12 * b + 2];
				vRow[8 * b + 1] = srcRow[12 * b + 2];
				vRow[8 * b + 2] = srcRow[12 * b + 2];
				vRow[8 * b + 3] = srcRow[12 * b + 2];
				yRow[8 * b + 1] = srcRow[12 * b + 3];
				uRow[8 * b + 4] = srcRow[12 * b + 4];
				uRow[8 * b + 5] = srcRow[12 * b + 4];
				uRow[8 * b + 6] = srcRow[12 * b + 4];
				uRow[8 * b + 7] = srcRow[12 * b + 4];
				yRow[8 * b + 2] = srcRow[12 * b + 5];
				vRow[8 * b + 4] = srcRow[12 * b + 6];
				vRow[8 * b + 5] = srcRow[12 * b + 6];
				vRow[8 * b + 6] = srcRow[12 * b + 6];
				vRow[8 * b + 7] = srcRow[12 * b + 6];
				yRow[8 * b + 3] = srcRow[12 * b + 7];
				yRow[8 * b + 4] = srcRow[12 * b + 8];
				yRow[8 * b + 5] = srcRow[12 * b + 9];
				yRow[8 * b + 6] = srcRow[12 * b + 10];
				yRow[8 * b + 7] = srcRow[12 * b + 11];
			}
		}
	}

	void yuv411Half(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		UInt srcStep = input.size() / inputSize.height;
		Int blocks = (Int)inputSize.width / 8;

		for (Int vSrc = 0, vDst = 0; vSrc < (Int)inputSize.height; vSrc += 2, vDst++)
		{
			Byte *srcRow1 = &srcData[(inputSize.height - vSrc - 1) * srcStep];
			Byte *srcRow2 = &srcData[(inputSize.height - vSrc - 2) * srcStep];
			Byte *yRow = &yData[vDst * yImage->widthStep];
			Byte *uRow = &uData[vDst * uImage->widthStep];
			Byte *vRow = &vData[vDst * vImage->widthStep];

			for (Int b = 0; b < blocks; b++)
			{
				Byte u0 = (Byte)(((UInt)srcRow1[12 * b] + (UInt)srcRow2[12 * b]) >> 1);
				Byte u1 = (Byte)(((UInt)srcRow1[12 * b + 4] + (UInt)srcRow2[12 * b + 4]) >> 1);
				Byte v0 = (Byte)(((UInt)srcRow1[12 * b + 2] + (UInt)srcRow2[12 * b + 2]) >> 1);
				Byte v1 = (Byte)(((UInt)srcRow1[12 * b + 6] + (UInt)srcRow2[12 * b + 6]) >> 1);

				uRow[4 * b] = u0;
				uRow[4 * b + 1] = u0;
				vRow[4 * b] = v0;
				vRow[4 * b + 1] = v0;
				uRow[4 * b + 2] = u1;
				uRow[4 * b + 3] = u1;
				vRow[4 * b + 2] = v1;
				vRow[4 * b + 3] = v1;
				yRow[4 * b] = (Byte)(((UInt)srcRow1[12 * b + 1] + (UInt)srcRow2[12 * b + 1] + (UInt)srcRow1[12 * b + 3] + (UInt)srcRow2[12 * b + 3]) >> 2);
				yRow[4 * b + 1] = (Byte)(((UInt)srcRow1[12 * b + 5] + (UInt)srcRow2[12 * b + 5] + (UInt)srcRow1[12 * b + 7] + (UInt)srcRow2[12 * b + 7]) >> 2);
				yRow[4 * b + 2] = (Byte)(((UInt)srcRow1[12 * b + 8] + (UInt)srcRow2[12 * b + 8] + (UInt)srcRow1[12 * b + 9] + (UInt)srcRow2[12 * b + 9]) >> 2);
				yRow[4 * b + 3] = (Byte)(((UInt)srcRow1[12 * b + 10] + (UInt)srcRow2[12 * b + 10] + (UInt)srcRow1[12 * b + 11] + (UInt)srcRow2[12 * b + 11]) >> 2);
			}
		}
	}

	void yuv411HalfSameSize(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		UInt srcStep = input.size() / inputSize.height;
		Int blocks = (Int)inputSize.width / 8;

		for (Int vSrc = 0, vDst = 0; vSrc < (Int)inputSize.height; vSrc += 2, vDst++)
		{
			Byte *srcRow1 = &srcData[(inputSize.height - vSrc - 1) * srcStep];
			Byte *srcRow2 = &srcData[(inputSize.height - vSrc - 2) * srcStep];
			Byte *yRow = &yData[vDst * yImage->widthStep];
			Byte *uRow = &uData[vDst * uImage->widthStep];
			Byte *vRow = &vData[vDst * vImage->widthStep];

			for (Int b = 0; b < blocks; b++)
			{
				uRow[2 * b] = (Byte)(((UInt)srcRow1[12 * b] + (UInt)srcRow2[12 * b]) >> 1);
				vRow[2 * b] = (Byte)(((UInt)srcRow1[12 * b + 2] + (UInt)srcRow2[12 * b + 2]) >> 1);
				uRow[2 * b + 1] = (Byte)(((UInt)srcRow1[12 * b + 4] + (UInt)srcRow2[12 * b + 4]) >> 1);
				vRow[2 * b + 1] = (Byte)(((UInt)srcRow1[12 * b + 6] + (UInt)srcRow2[12 * b + 6]) >> 1);
				yRow[4 * b] = (Byte)(((UInt)srcRow1[12 * b + 1] + (UInt)srcRow2[12 * b + 1] + (UInt)srcRow1[12 * b + 3] + (UInt)srcRow2[12 * b + 3]) >> 2);
				yRow[4 * b + 1] = (Byte)(((UInt)srcRow1[12 * b + 5] + (UInt)srcRow2[12 * b + 5] + (UInt)srcRow1[12 * b + 7] + (UInt)srcRow2[12 * b + 7]) >> 2);
				yRow[4 * b + 2] = (Byte)(((UInt)srcRow1[12 * b + 8] + (UInt)srcRow2[12 * b + 8] + (UInt)srcRow1[12 * b + 9] + (UInt)srcRow2[12 * b + 9]) >> 2);
				yRow[4 * b + 3] = (Byte)(((UInt)srcRow1[12 * b + 10] + (UInt)srcRow2[12 * b + 10] + (UInt)srcRow1[12 * b + 11] + (UInt)srcRow2[12 * b + 11]) >> 2);
			}
		}
	}

	void yuv411Quarter(Binary input, Size2D inputSize, StructImage *yImage, StructImage *uImage, StructImage *vImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;

		UInt srcStep = input.size() / inputSize.height;
		Int blocks = (Int)inputSize.width / 8;

		for (Int vSrc = 0, vDst = 0; vSrc < (Int)inputSize.height; vSrc += 4, vDst++)
		{
			Byte *srcRow1 = &srcData[(inputSize.height - vSrc - 1) * srcStep];
			Byte *srcRow2 = &srcData[(inputSize.height - vSrc - 2) * srcStep];
			Byte *srcRow3 = &srcData[(inputSize.height - vSrc - 3) * srcStep];
			Byte *srcRow4 = &srcData[(inputSize.height - vSrc - 4) * srcStep];
			Byte *yRow = &yData[vDst * yImage->widthStep];
			Byte *uRow = &uData[vDst * uImage->widthStep];
			Byte *vRow = &vData[vDst * vImage->widthStep];

			for (Int b = 0; b < blocks; b++)
			{
				uRow[2 * b] = (Byte)(((UInt)srcRow1[12 * b] + (UInt)srcRow2[12 * b] + (UInt)srcRow3[12 * b] + (UInt)srcRow4[12 * b]) >> 2);
				vRow[2 * b] = (Byte)(((UInt)srcRow1[12 * b + 2] + (UInt)srcRow2[12 * b + 2] + (UInt)srcRow3[12 * b + 2] + (UInt)srcRow4[12 * b + 2]) >> 2);
				uRow[2 * b + 1] = (Byte)(((UInt)srcRow1[12 * b + 4] + (UInt)srcRow2[12 * b + 4] + (UInt)srcRow3[12 * b + 4] + (UInt)srcRow4[12 * b + 4]) >> 2);
				vRow[2 * b + 1] = (Byte)(((UInt)srcRow1[12 * b + 6] + (UInt)srcRow2[12 * b + 6] + (UInt)srcRow3[12 * b + 6] + (UInt)srcRow4[12 * b + 6]) >> 2);
				yRow[2 * b] = (Byte)(((UInt)srcRow1[12 * b + 1] + (UInt)srcRow2[12 * b + 1] + (UInt)srcRow3[12 * b + 1] + (UInt)srcRow4[12 * b + 1]
					+ (UInt)srcRow1[12 * b + 3] + (UInt)srcRow2[12 * b + 3] + (UInt)srcRow3[12 * b + 3] + (UInt)srcRow4[12 * b + 3]
					+ (UInt)srcRow1[12 * b + 5] + (UInt)srcRow2[12 * b + 5] + (UInt)srcRow3[12 * b + 5] + (UInt)srcRow4[12 * b + 5]
					+ (UInt)srcRow1[12 * b + 7] + (UInt)srcRow2[12 * b + 7] + (UInt)srcRow3[12 * b + 7] + (UInt)srcRow4[12 * b + 7]) >> 4);
				yRow[2 * b + 1] = (Byte)(((UInt)srcRow1[12 * b + 8] + (UInt)srcRow2[12 * b + 8] + (UInt)srcRow3[12 * b + 8] + (UInt)srcRow4[12 * b + 8]
					+ (UInt)srcRow1[12 * b + 9] + (UInt)srcRow2[12 * b + 9] + (UInt)srcRow3[12 * b + 9] + (UInt)srcRow4[12 * b + 9]
					+ (UInt)srcRow1[12 * b + 10] + (UInt)srcRow2[12 * b + 10] + (UInt)srcRow3[12 * b + 10] + (UInt)srcRow4[12 * b + 10]
					+ (UInt)srcRow1[12 * b + 11] + (UInt)srcRow2[12 * b + 11] + (UInt)srcRow3[12 * b + 11] + (UInt)srcRow4[12 * b + 11]) >> 4);
			}
		}
	}

	void resizeChannelHalf(CompatibleChannelData src, CompatibleChannelData dst)
	{
		const UInt dstWidth = dst.width();
		const UInt dstHeight = dst.height();
		const UInt srcStep = src.step();
		const UInt dstStep = dst.step();
		const UInt rowBlock = dstWidth / 4;
		const Byte *srcData = src.data();
		Byte *dstData = dst.data();

		for (UInt v = 0; v < dstHeight; v++)
		{
			const UInt *srcRow1 = (const UInt*)&srcData[2 * v * srcStep];
			const UInt *srcRow2 = (const UInt*)&srcData[(2 * v + 1) * srcStep];
			UInt *dstRow = (UInt*)&dstData[v * dstStep];

			UInt i1 = 0, i2 = 0, row1b1, row1b2, row2b1, row2b2;
			for (UInt b = 0; b < rowBlock; b++)
			{
				row1b1 = srcRow1[i1++];
				row1b2 = srcRow1[i1++];
				row2b1 = srcRow2[i2++];
				row2b2 = srcRow2[i2++];
				dstRow[b] = (((row1b1 & 0x000000ff) + ((row1b1 & 0x0000ff00) >> 8) + (row2b1 & 0x000000ff) + ((row2b1 & 0x0000ff00) >> 8)) >> 2) |
					(((((row1b1 & 0x00ff0000) >> 16) + ((row1b1 & 0xff000000) >> 24) + ((row2b1 & 0x00ff0000) >> 16) + ((row2b1 & 0xff000000) >> 24)) >> 2) << 8) |
					((((row1b2 & 0x000000ff) + ((row1b2 & 0x0000ff00) >> 8) + (row2b2 & 0x000000ff) + ((row2b2 & 0x0000ff00) >> 8)) >> 2) << 16) |
					(((((row1b2 & 0x00ff0000) >> 16) + ((row1b2 & 0xff000000) >> 24) + ((row2b2 & 0x00ff0000) >> 16) + ((row2b2 & 0xff000000) >> 24)) >> 2) << 24);
			}
		}
	}

	void resizeChannelOneThird(CompatibleChannelData src, CompatibleChannelData dst)
	{
		const UInt dstWidth = dst.width();
		const UInt dstHeight = dst.height();
		const UInt srcStep = src.step();
		const UInt dstStep = dst.step();
		const UInt rowBlock = dstWidth / 4;
		const Byte *srcData = src.data();
		Byte *dstData = dst.data();

		for (UInt v = 0; v < dstHeight; v++)
		{
			const UInt *srcRow1 = (const UInt*)&srcData[3 * v * srcStep];
			const UInt *srcRow2 = (const UInt*)&srcData[(3 * v + 1) * srcStep];
			const UInt *srcRow3 = (const UInt*)&srcData[(3 * v + 2) * srcStep];
			UInt *dstRow = (UInt*)&dstData[v * dstStep];

			UInt i1 = 0, i2 = 0, i3 = 0, row1b1, row1b2, row1b3, row2b1, row2b2, row2b3, row3b1, row3b2, row3b3;
			for (UInt b = 0; b < rowBlock; b++)
			{
				row1b1 = srcRow1[i1++];
				row1b2 = srcRow1[i1++];
				row1b3 = srcRow1[i1++];
				row2b1 = srcRow2[i2++];
				row2b2 = srcRow2[i2++];
				row2b3 = srcRow2[i2++];
				row3b1 = srcRow3[i3++];
				row3b2 = srcRow3[i3++];
				row3b3 = srcRow3[i3++];

				dstRow[b] = (((row1b1 & 0x000000ff) + ((row1b1 & 0x0000ff00) >> 8) + ((row1b1 & 0x00ff0000) >> 16) + (row2b1 & 0x000000ff) +
					((row2b1 & 0x00ff0000) >> 16) + (row3b1 & 0x000000ff) + ((row3b1 & 0x0000ff00) >> 8) + ((row3b1 & 0x00ff0000) >> 16)) >> 3) |
					(((((row1b1 & 0xff000000) >> 24) + (row1b2 & 0x000000ff) + ((row1b2 & 0x0000ff00) >> 8) + ((row2b1 & 0xff000000) >> 24) +
					((row2b2 & 0x0000ff00) >> 8) + ((row3b1 & 0xff000000) >> 24) + (row3b2 & 0x000000ff) + ((row3b2 & 0x0000ff00) >> 8)) >> 3) << 8) |
					(((((row1b2 & 0x00ff0000) >> 16) + ((row1b2 & 0xff000000) >> 24) + (row1b3 & 0x000000ff) + ((row2b2 & 0x00ff0000) >> 16) +
					(row2b3 & 0x000000ff) + ((row3b2 & 0x00ff0000) >> 16) + ((row3b2 & 0xff000000) >> 24) + (row3b3 & 0x000000ff)) >> 3) << 16) |
					(((((row1b3 & 0x0000ff00) >> 8) + ((row1b3 & 0x00ff0000) >> 16) + ((row1b3 & 0xff000000) >> 24) + ((row2b3 & 0x0000ff00) >> 8) +
					((row2b3 & 0xff000000) >> 24) + ((row3b3 & 0x0000ff00) >> 8) + ((row3b3 & 0x00ff0000) >> 16) + ((row3b3 & 0xff000000) >> 24)) >> 3) << 24);
			}
		}
	}

	void resizeChannelQuarter(CompatibleChannelData src, CompatibleChannelData dst)
	{
		const UInt dstWidth = dst.width();
		const UInt dstHeight = dst.height();
		const UInt srcStep = src.step();
		const UInt dstStep = dst.step();
		const Byte *srcData = src.data();
		Byte *dstData = dst.data();

		for (UInt v = 0; v < dstHeight; v++)
		{
			const UInt *srcRow1 = (const UInt*)&srcData[4 * v * srcStep];
			const UInt *srcRow2 = (const UInt*)&srcData[(4 * v + 1) * srcStep];
			const UInt *srcRow3 = (const UInt*)&srcData[(4 * v + 2) * srcStep];
			const UInt *srcRow4 = (const UInt*)&srcData[(4 * v + 3) * srcStep];
			Byte *dstRow = (Byte*)&dstData[v * dstStep];

			UInt b1, b2, b3, b4;
			for (UInt u = 0; u < dstWidth; u++)
			{
				b1 = srcRow1[u];
				b2 = srcRow2[u];
				b3 = srcRow3[u];
				b4 = srcRow4[u];
				*dstRow++ = (Byte)(((b1 & 0x000000ff) + ((b1 & 0x0000ff00) >> 8) + ((b1 & 0x00ff0000) >> 16) + ((b1 & 0xff000000) >> 24) + 
							(b2 & 0x000000ff) + ((b2 & 0x0000ff00) >> 8) + ((b2 & 0x00ff0000) >> 16) + ((b2 & 0xff000000) >> 24) + 
							(b3 & 0x000000ff) + ((b3 & 0x0000ff00) >> 8) + ((b3 & 0x00ff0000) >> 16) + ((b3 & 0xff000000) >> 24) + 
							(b4 & 0x000000ff) + ((b4 & 0x0000ff00) >> 8) + ((b4 & 0x00ff0000) >> 16) + ((b4 & 0xff000000) >> 24)) >> 4);
			}
		}
	}

	void resizeChannelOneSixth(CompatibleChannelData src, CompatibleChannelData dst)
	{
		const UInt dstWidth = dst.width();
		const UInt dstHeight = dst.height();
		const UInt srcStep = src.step();
		const UInt dstStep = dst.step();
		const UInt rowBlock = dstWidth / 2;
		const Byte* srcData = src.data();
		Byte* dstData = dst.data();

		for (UInt v = 0; v < dstHeight; v++)
		{
			const UInt* srcRow1 = (const UInt*)&srcData[6 * v * srcStep];
			const UInt* srcRow2 = (const UInt*)&srcData[(6 * v + 1) * srcStep];
			const UInt* srcRow3 = (const UInt*)&srcData[(6 * v + 2) * srcStep];
			const UInt* srcRow4 = (const UInt*)&srcData[(6 * v + 3) * srcStep];
			const UInt* srcRow5 = (const UInt*)&srcData[(6 * v + 4) * srcStep];
			const UInt* srcRow6 = (const UInt*)&srcData[(6 * v + 5) * srcStep];
			Byte* dstRow = (Byte*)&dstData[v * dstStep];

			UInt i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0,
				row1b1, row1b2, row1b3, row2b1, row2b2, row2b3, row3b1, row3b2, row3b3,
				row4b1, row4b2, row4b3, row5b1, row5b2, row5b3, row6b1, row6b2, row6b3;
			for (UInt b = 0; b < rowBlock; b++)
			{
				row1b1 = srcRow1[i1++];
				row1b2 = srcRow1[i1++];
				row1b3 = srcRow1[i1++];
				row2b1 = srcRow2[i2++];
				row2b2 = srcRow2[i2++];
				row2b3 = srcRow2[i2++];
				row3b1 = srcRow3[i3++];
				row3b2 = srcRow3[i3++];
				row3b3 = srcRow3[i3++];
				row4b1 = srcRow4[i4++];
				row4b2 = srcRow4[i4++];
				row4b3 = srcRow4[i4++];
				row5b1 = srcRow5[i5++];
				row5b2 = srcRow5[i5++];
				row5b3 = srcRow5[i5++];
				row6b1 = srcRow6[i6++];
				row6b2 = srcRow6[i6++];
				row6b3 = srcRow6[i6++];

				dstRow[2 * b] = (Byte)(((row1b1 & 0x000000ff) + ((row1b1 & 0x0000ff00) >> 8) + ((row1b1 & 0x00ff0000) >> 16) + ((row1b1 & 0xff000000) >> 24) + (row1b2 & 0x000000ff) + ((row1b2 & 0x0000ff00) >> 8) +
					(row2b1 & 0x000000ff) + ((row2b1 & 0x00ff0000) >> 16) + ((row2b1 & 0xff000000) >> 24) + ((row2b2 & 0x0000ff00) >> 8) +
					(row3b1 & 0x000000ff) + ((row3b1 & 0x0000ff00) >> 8) + ((row3b1 & 0x00ff0000) >> 16) + ((row3b1 & 0xff000000) >> 24) + (row3b2 & 0x000000ff) + ((row3b2 & 0x0000ff00) >> 8) +
					(row4b1 & 0x000000ff) + ((row4b1 & 0x0000ff00) >> 8) + ((row4b1 & 0x00ff0000) >> 16) + ((row4b1 & 0xff000000) >> 24) + (row4b2 & 0x000000ff) + ((row4b2 & 0x0000ff00) >> 8) +
					(row5b1 & 0x000000ff) + ((row5b1 & 0x00ff0000) >> 16) + ((row5b1 & 0xff000000) >> 24) + ((row5b2 & 0x0000ff00) >> 8) +
					(row6b1 & 0x000000ff) + ((row6b1 & 0x0000ff00) >> 8) + ((row6b1 & 0x00ff0000) >> 16) + ((row6b1 & 0xff000000) >> 24) + (row6b2 & 0x000000ff) + ((row6b2 & 0x0000ff00) >> 8)) >> 5);

				dstRow[2 * b + 1] = (Byte)((((row1b2 & 0x00ff0000) >> 16) + ((row1b2 & 0xff000000) >> 24) + (row1b3 & 0x000000ff) + ((row1b3 & 0x0000ff00) >> 8) + ((row1b3 & 0x00ff0000) >> 16) + ((row1b3 & 0xff000000) >> 24) +
					((row2b2 & 0x00ff0000) >> 16) + (row2b3 & 0x000000ff) + ((row2b3 & 0x0000ff00) >> 8) + ((row2b3 & 0xff000000) >> 24) +
					((row3b2 & 0x00ff0000) >> 16) + ((row3b2 & 0xff000000) >> 24) + (row3b3 & 0x000000ff) + ((row3b3 & 0x0000ff00) >> 8) + ((row3b3 & 0x00ff0000) >> 16) + ((row3b3 & 0xff000000) >> 24) +
					((row4b2 & 0x00ff0000) >> 16) + ((row4b2 & 0xff000000) >> 24) + (row4b3 & 0x000000ff) + ((row4b3 & 0x0000ff00) >> 8) + ((row4b3 & 0x00ff0000) >> 16) + ((row4b3 & 0xff000000) >> 24) +
					((row5b2 & 0x00ff0000) >> 16) + (row5b3 & 0x000000ff) + ((row5b3 & 0x0000ff00) >> 8) + ((row5b3 & 0xff000000) >> 24) +
					((row6b2 & 0x00ff0000) >> 16) + ((row6b2 & 0xff000000) >> 24) + (row6b3 & 0x000000ff) + ((row6b3 & 0x0000ff00) >> 8) + ((row6b3 & 0x00ff0000) >> 16) + ((row6b3 & 0xff000000) >> 24)) >> 5);
			}
		}
	}

	void resizeChannelOneEighth(CompatibleChannelData src, CompatibleChannelData dst)
	{
		const UInt dstWidth = dst.width();
		const UInt dstHeight = dst.height();
		const UInt srcStep = src.step();
		const UInt dstStep = dst.step();
		const Byte* srcData = src.data();
		Byte* dstData = dst.data();

		for (UInt v = 0; v < dstHeight; v++)
		{
			const ULong* srcRow1 = (const ULong*)&srcData[8 * v * srcStep];
			const ULong* srcRow2 = (const ULong*)&srcData[(8 * v + 1) * srcStep];
			const ULong* srcRow3 = (const ULong*)&srcData[(8 * v + 2) * srcStep];
			const ULong* srcRow4 = (const ULong*)&srcData[(8 * v + 3) * srcStep];
			const ULong* srcRow5 = (const ULong*)&srcData[(8 * v + 4) * srcStep];
			const ULong* srcRow6 = (const ULong*)&srcData[(8 * v + 5) * srcStep];
			const ULong* srcRow7 = (const ULong*)&srcData[(8 * v + 6) * srcStep];
			const ULong* srcRow8 = (const ULong*)&srcData[(8 * v + 7) * srcStep];
			Byte* dstRow = (Byte*)&dstData[v * dstStep];

			for (UInt u = 0; u < dstWidth; u++)
			{
				dstRow[u] = (Byte)(((srcRow1[u] & 0x00000000000000ffull) + ((srcRow1[u] & 0x000000000000ff00ull) >> 8) + ((srcRow1[u] & 0x0000000000ff0000ull) >> 16) + ((srcRow1[u] & 0x00000000ff000000ull) >> 24) + ((srcRow1[u] & 0x000000ff00000000ull) >> 32) + ((srcRow1[u] & 0x0000ff0000000000ull) >> 40) + ((srcRow1[u] & 0x00ff000000000000ull) >> 48) + ((srcRow1[u] & 0xff00000000000000ull) >> 56) + 
							(srcRow2[u] & 0x00000000000000ffull) + ((srcRow2[u] & 0x000000000000ff00ull) >> 8) + ((srcRow2[u] & 0x0000000000ff0000ull) >> 16) + ((srcRow2[u] & 0x00000000ff000000ull) >> 24) + ((srcRow2[u] & 0x000000ff00000000ull) >> 32) + ((srcRow2[u] & 0x0000ff0000000000ull) >> 40) + ((srcRow2[u] & 0x00ff000000000000ull) >> 48) + ((srcRow2[u] & 0xff00000000000000ull) >> 56) + 
							(srcRow3[u] & 0x00000000000000ffull) + ((srcRow3[u] & 0x000000000000ff00ull) >> 8) + ((srcRow3[u] & 0x0000000000ff0000ull) >> 16) + ((srcRow3[u] & 0x00000000ff000000ull) >> 24) + ((srcRow3[u] & 0x000000ff00000000ull) >> 32) + ((srcRow3[u] & 0x0000ff0000000000ull) >> 40) + ((srcRow3[u] & 0x00ff000000000000ull) >> 48) + ((srcRow3[u] & 0xff00000000000000ull) >> 56) + 
							(srcRow4[u] & 0x00000000000000ffull) + ((srcRow4[u] & 0x000000000000ff00ull) >> 8) + ((srcRow4[u] & 0x0000000000ff0000ull) >> 16) + ((srcRow4[u] & 0x00000000ff000000ull) >> 24) + ((srcRow4[u] & 0x000000ff00000000ull) >> 32) + ((srcRow4[u] & 0x0000ff0000000000ull) >> 40) + ((srcRow4[u] & 0x00ff000000000000ull) >> 48) + ((srcRow4[u] & 0xff00000000000000ull) >> 56) + 
							(srcRow5[u] & 0x00000000000000ffull) + ((srcRow5[u] & 0x000000000000ff00ull) >> 8) + ((srcRow5[u] & 0x0000000000ff0000ull) >> 16) + ((srcRow5[u] & 0x00000000ff000000ull) >> 24) + ((srcRow5[u] & 0x000000ff00000000ull) >> 32) + ((srcRow5[u] & 0x0000ff0000000000ull) >> 40) + ((srcRow5[u] & 0x00ff000000000000ull) >> 48) + ((srcRow5[u] & 0xff00000000000000ull) >> 56) + 
							(srcRow6[u] & 0x00000000000000ffull) + ((srcRow6[u] & 0x000000000000ff00ull) >> 8) + ((srcRow6[u] & 0x0000000000ff0000ull) >> 16) + ((srcRow6[u] & 0x00000000ff000000ull) >> 24) + ((srcRow6[u] & 0x000000ff00000000ull) >> 32) + ((srcRow6[u] & 0x0000ff0000000000ull) >> 40) + ((srcRow6[u] & 0x00ff000000000000ull) >> 48) + ((srcRow6[u] & 0xff00000000000000ull) >> 56) + 
							(srcRow7[u] & 0x00000000000000ffull) + ((srcRow7[u] & 0x000000000000ff00ull) >> 8) + ((srcRow7[u] & 0x0000000000ff0000ull) >> 16) + ((srcRow7[u] & 0x00000000ff000000ull) >> 24) + ((srcRow7[u] & 0x000000ff00000000ull) >> 32) + ((srcRow7[u] & 0x0000ff0000000000ull) >> 40) + ((srcRow7[u] & 0x00ff000000000000ull) >> 48) + ((srcRow7[u] & 0xff00000000000000ull) >> 56) + 
							(srcRow8[u] & 0x00000000000000ffull) + ((srcRow8[u] & 0x000000000000ff00ull) >> 8) + ((srcRow8[u] & 0x0000000000ff0000ull) >> 16) + ((srcRow8[u] & 0x00000000ff000000ull) >> 24) + ((srcRow8[u] & 0x000000ff00000000ull) >> 32) + ((srcRow8[u] & 0x0000ff0000000000ull) >> 40) + ((srcRow8[u] & 0x00ff000000000000ull) >> 48) + ((srcRow8[u] & 0xff00000000000000ull) >> 56)) >> 6);
			}
		}
	}

	void yuv420ToYuv4p(StructImage* yImage, StructImage* uImage, StructImage* vImage, Byte* outputData)
	{
		Int widthHalf = yImage->width / 2;
		Int heightHalf = yImage->height / 2;
		Int yStep = yImage->widthStep;
		Int uvStep = uImage->widthStep;
		Byte* yData = (Byte*)yImage->imageData;
		Byte* uData = (Byte*)uImage->imageData;
		Byte* vData = (Byte*)vImage->imageData;

		for (Int v = 0; v < heightHalf; v++)
		{
			Byte* y1Ptr = yData + 2 * v * yStep;
			Byte* y2Ptr = yData + (2 * v + 1) * yStep;
			Byte* uPtr = uData + v * uvStep;
			Byte* vPtr = vData + v * uvStep;
			for (Int u = 0; u < widthHalf; u++)
			{
				*(outputData++) = *(uPtr++) ^ 0x80;
				*(outputData++) = *(vPtr++) ^ 0x80;
				*(outputData++) = *(y1Ptr++);
				*(outputData++) = *(y1Ptr++);
				*(outputData++) = *(y2Ptr++);
				*(outputData++) = *(y2Ptr++);
			}
		}
	}

	void yuv420ToBgr(StructImage* yImage, StructImage* uImage, StructImage* vImage, StructImage *bgrImage)
	{
		Byte *yData = (Byte*)yImage->imageData;
		Byte *uData = (Byte*)uImage->imageData;
		Byte *vData = (Byte*)vImage->imageData;
		Byte *bgrData = (Byte*)bgrImage->imageData;

		Int halfWidth = yImage->width / 2;
		Int halfHeight = yImage->height / 2;

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *yRow1 = yData + 2 * v * yImage->widthStep;
			Byte *yRow2 = yData + (2 * v + 1) * yImage->widthStep;
			Byte *uRow = uData + v * uImage->widthStep;
			Byte *vRow = vData + v * vImage->widthStep;
			Byte *bgrRow1 = bgrData + 2 * v * bgrImage->widthStep;
			Byte *bgrRow2 = bgrData + (2 * v + 1) * bgrImage->widthStep;

			for (Int u = 0; u < halfWidth; u++)
			{
				Int uVal = *uRow++;
				Int vVal = *vRow++;
				Int bColorComp = uVal * 116129 - 14864512;
				Int gColorComp = -uVal * 22553 - vVal * 46801 + 8877312;
				Int rColorComp = vVal * 91881 - 11760768;

				Int yVal = (*yRow1++) * 65536;
				*bgrRow1++ = (Byte)(math::clamp(yVal + bColorComp, 0, 16711680) >> 16);
				*bgrRow1++ = (Byte)(math::clamp(yVal + gColorComp, 0, 16711680) >> 16);
				*bgrRow1++ = (Byte)(math::clamp(yVal + rColorComp, 0, 16711680) >> 16);

				yVal = (*yRow1++) * 65536;
				*bgrRow1++ = (Byte)(math::clamp(yVal + bColorComp, 0, 16711680) >> 16);
				*bgrRow1++ = (Byte)(math::clamp(yVal + gColorComp, 0, 16711680) >> 16);
				*bgrRow1++ = (Byte)(math::clamp(yVal + rColorComp, 0, 16711680) >> 16);

				yVal = (*yRow2++) * 65536;
				*bgrRow2++ = (Byte)(math::clamp(yVal + bColorComp, 0, 16711680) >> 16);
				*bgrRow2++ = (Byte)(math::clamp(yVal + gColorComp, 0, 16711680) >> 16);
				*bgrRow2++ = (Byte)(math::clamp(yVal + rColorComp, 0, 16711680) >> 16);

				yVal = (*yRow2++) * 65536;
				*bgrRow2++ = (Byte)(math::clamp(yVal + bColorComp, 0, 16711680) >> 16);
				*bgrRow2++ = (Byte)(math::clamp(yVal + gColorComp, 0, 16711680) >> 16);
				*bgrRow2++ = (Byte)(math::clamp(yVal + rColorComp, 0, 16711680) >> 16);
			}
		}
	}

	void raw8ToBgr(Binary input, Size2D inputSize, StructImage *bgrImage)
	{
		Byte *srcData = (Byte*)input.data();
		Byte *bgrData = (Byte*)bgrImage->imageData;

		Int width = (Int)inputSize.width;
		Int halfHeight = (Int)inputSize.height / 2;
		UInt srcStep = input.size() / inputSize.height;

		for (Int v = 0; v < halfHeight; v++)
		{
			Byte *srcRow1 = (Byte*)&srcData[2 * v * srcStep];
			Byte *srcRow2 = (Byte*)&srcData[(2 * v + 1) * srcStep];
			Byte *dstRow1 = (Byte*)&bgrData[2 * v * bgrImage->widthStep];
			Byte *dstRow2 = (Byte*)&bgrData[(2 * v + 1) * bgrImage->widthStep];

			Int val11 = (Int)srcRow1[0], val12 = (Int)srcRow1[1], val21 = (Int)srcRow2[0], val22 = (Int)srcRow2[1];
			*dstRow1++ = val11; *dstRow1++ = 0; *dstRow1++ = 0;
			*dstRow2++ = 0; *dstRow2++ = val21; *dstRow2++ = 0;

			Bool mode = TRUE;
			for (Int u = 2; u < width; u++)
			{
				Int val13 = (Int)srcRow1[u];
				Int val23 = (Int)srcRow2[u];
				if (mode)
				{
					Byte commonB = (Byte)((val11 + val13) >> 1);
					Byte commonR = (Byte)val22;
					*dstRow1++ = commonB;
					*dstRow1++ = (Byte)val12;
					*dstRow1++ = commonR;
					*dstRow2++ = commonB;
					*dstRow2++ = (Byte)((val21 + val23) >> 1);
					*dstRow2++ = commonR;
				}
				else
				{
					Byte commonB = (Byte)val12;
					Byte commonR = (Byte)((val21 + val23) >> 1);
					*dstRow1++ = commonB;
					*dstRow1++ = (Byte)((val11 + val13) >> 1);
					*dstRow1++ = commonR;
					*dstRow2++ = commonB;
					*dstRow2++ = (Byte)val22;
					*dstRow2++ = commonR;
				}
				val11 = val12;
				val12 = val13;
				val21 = val22;
				val22 = val23;
				mode = !mode;
			}
			if (mode)
			{
				*dstRow1++ = 0; *dstRow1++ = val12; *dstRow1++ = 0;
				*dstRow2++ = 0; *dstRow2++ = 0; *dstRow2++ = val22;
			}
			else
			{
				*dstRow1++ = val12; *dstRow1++ = 0; *dstRow1++ = 0;
				*dstRow2++ = 0; *dstRow2++ = val22; *dstRow2++ = 0;
			}
		}
	}

	StructImage renewImagePointer(CompatibleImage& target, UInt width, UInt height, Byte initValue)
	{
		if (target.getType() != CompatibleImageType::ImagePointerGray || target.width() != width || target.height() != height)
		{
			ImagePointer newImagePtr(Size2D::wh(width, height), FALSE);
			utility::memorySet(initValue, newImagePtr.getData().data(), newImagePtr.getHeight() * newImagePtr.getRowBytes());
			target = CompatibleImage(newImagePtr);
		}
		StructImage output;
		convStructImage(target.channels()[0], output);
		return output;
	}

	Bool isDividable(Size2D size, UInt number)
	{
		return size.width % number == 0 && size.height % number == 0;
	}
}

using namespace videonext;

void videonext::calcSplitYuv420Size(Size2D rawSize, UInt forWidth, Size2D& ySize, Size2D& uSize, Size2D& vSize)
{
	Bool canThird = isDividable(rawSize, 3);
	Bool canQuarter = isDividable(rawSize, 4);
	Float ratio = (Float)rawSize.width / forWidth;

	if (ratio >= 4 && canQuarter)
	{
		ySize.width = rawSize.width / 4;
		ySize.height = rawSize.height / 4;
	}
	else if (ratio >= 3 && canThird)
	{
		ySize.width = rawSize.width / 3;
		ySize.height = rawSize.height / 3;
	}
	else if (ratio >= 2)
	{
		ySize.width = rawSize.width / 2;
		ySize.height = rawSize.height / 2;
	}
	else
	{
		ySize = rawSize;
	}

	if (ratio >= 4 && canQuarter)
	{
		uSize.width = rawSize.width / 4;
		uSize.height = rawSize.height / 4;
		vSize.width = rawSize.width / 4;
		vSize.height = rawSize.height / 4;
	}
	else
	{
		uSize.width = rawSize.width / 2;
		uSize.height = rawSize.height / 2;
		vSize.width = rawSize.width / 2;
		vSize.height = rawSize.height / 2;
	}
}
void videonext::calcSplitNv12Size(Size2D rawSize, UInt forWidth, Size2D& ySize, Size2D& uSize, Size2D& vSize)
{
	calcSplitYuv420Size(rawSize, forWidth, ySize, uSize, vSize);
}
void videonext::calcSplitYuv411Size(Size2D rawSize, UInt forWidth, Size2D& ySize, Size2D& uSize, Size2D& vSize)
{
	Float ratio = (Float)rawSize.width / forWidth;

	if (ratio >= 4)
	{
		ySize.width = rawSize.width / 4;
		ySize.height = rawSize.height / 4;
		uSize.width = rawSize.width / 4;
		uSize.height = rawSize.height / 4;
		vSize.width = rawSize.width / 4;
		vSize.height = rawSize.height / 4;
	}
	else if (ratio >= 2)
	{
		ySize.width = rawSize.width / 2;
		ySize.height = rawSize.height / 2;
		uSize.width = rawSize.width / 4;
		uSize.height = rawSize.height / 2;
		vSize.width = rawSize.width / 4;
		vSize.height = rawSize.height / 2;
	}
	else
	{
		ySize = rawSize;
		uSize.width = rawSize.width / 4;
		uSize.height = rawSize.height;
		vSize.width = rawSize.width / 4;
		vSize.height = rawSize.height;
	}
}

Bool videonext::bgrToYuv420(CompatibleImage input, CompatibleImage& output)
{
	if (input.getType() != CompatibleImageType::ImagePointerColor && input.getType() != CompatibleImageType::IplImageColor) return FALSE;

	UInt width = input.width();
	UInt height = input.height();
	CompatibleChannelData channel = input.channels()[0];

	if (output.width() != width || output.height() != height || output.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		output = CompatibleImage(CompatibleImageType::AVFrameYUV420P, input.size());
	}

	Array<CompatibleChannelData> channels = output.channels();
	StructImage yImage, uImage, vImage;
	if (!convStructImage(channels[0], yImage) || !convStructImage(channels[1], uImage) || !convStructImage(channels[2], vImage)) return FALSE;

	Byte *srcData = (Byte*)channel.data();
	UInt halfWidth = width / 2;
	UInt halfHeight = height / 2;

	for (UInt v = 0; v < halfHeight; v++)
	{
		Byte *src1 = &srcData[(2 * v) * channel.step()];
		Byte *src2 = &srcData[(2 * v + 1) * channel.step()];
		Byte *dstY1 = &((Byte*)yImage.imageData)[(2 * v) * yImage.widthStep];
		Byte *dstY2 = &((Byte*)yImage.imageData)[(2 * v + 1) * yImage.widthStep];
		Byte *dstU = &((Byte*)uImage.imageData)[v * uImage.widthStep];
		Byte *dstV = &((Byte*)vImage.imageData)[v * vImage.widthStep];

		for (UInt u = 0; u < halfWidth; u++)
		{
			Int b00 = src1[6 * u];
			Int g00 = src1[6 * u + 1];
			Int r00 = src1[6 * u + 2];
			Int b01 = src1[6 * u + 3];
			Int g01 = src1[6 * u + 4];
			Int r01 = src1[6 * u + 5];
			Int b10 = src2[6 * u];
			Int g10 = src2[6 * u + 1];
			Int r10 = src2[6 * u + 2];
			Int b11 = src2[6 * u + 3];
			Int g11 = src2[6 * u + 4];
			Int r11 = src2[6 * u + 5];

			dstU[u] = (Byte)(((r00 + r01 + r10 + r11) * (-11058) + (g00 + g01 + g10 + g11) * (-21709) + ((b00 + b01 + b10 + b11) << 15) + 33554432) >> 18);
			dstV[u] = (Byte)(((b00 + b01 + b10 + b11) * (-5328) + (g00 + g01 + g10 + g11) * (-27438) + ((r00 + r01 + r10 + r11) << 15) + 33554432) >> 18);
			dstY1[2 * u] = (Byte)((r00 * 19589 + g00 * 38443 + b00 * 7502) >> 16);
			dstY1[2 * u + 1] = (Byte)((r01 * 19589 + g01 * 38443 + b01 * 7502) >> 16);
			dstY2[2 * u] = (Byte)((r10 * 19589 + g10 * 38443 + b10 * 7502) >> 16);
			dstY2[2 * u + 1] = (Byte)((r11 * 19589 + g11 * 38443 + b11 * 7502) >> 16);
		}
	}

	return TRUE;
}

Bool videonext::bgrToYuv411(CompatibleImage input, Binary& output)
{
	if (input.getType() != CompatibleImageType::ImagePointerColor && input.getType() != CompatibleImageType::IplImageColor) return FALSE;

	UInt width = input.width();
	UInt height = input.height();
	CompatibleChannelData channel = input.channels()[0];

	UInt outputSize = height * width * 3 / 2;
	if (output.size() != outputSize) output = Binary(outputSize);

	Byte *srcData = (Byte*)channel.data();
	UInt nBlocks = width / 8;

	for (UInt v = 0; v < height; v++)
	{
		Byte *src = &srcData[v * channel.step()];
		Byte *dst = &output[(height - 1 - v) * width * 3 / 2];

		for (UInt u = 0; u < nBlocks; u++)
		{
			Int b0 = src[24 * u];
			Int g0 = src[24 * u + 1];
			Int r0 = src[24 * u + 2];
			Int b1 = src[24 * u + 3];
			Int g1 = src[24 * u + 4];
			Int r1 = src[24 * u + 5];
			Int b2 = src[24 * u + 6];
			Int g2 = src[24 * u + 7];
			Int r2 = src[24 * u + 8];
			Int b3 = src[24 * u + 9];
			Int g3 = src[24 * u + 10];
			Int r3 = src[24 * u + 11];
			Int b4 = src[24 * u + 12];
			Int g4 = src[24 * u + 13];
			Int r4 = src[24 * u + 14];
			Int b5 = src[24 * u + 15];
			Int g5 = src[24 * u + 16];
			Int r5 = src[24 * u + 17];
			Int b6 = src[24 * u + 18];
			Int g6 = src[24 * u + 19];
			Int r6 = src[24 * u + 20];
			Int b7 = src[24 * u + 21];
			Int g7 = src[24 * u + 22];
			Int r7 = src[24 * u + 23];

			dst[12 * u] = (Byte)(((r0 + r1 + r2 + r3) * (-11058) + (g0 + g1 + g2 + g3) * (-21709) + ((b0 + b1 + b2 + b3) << 15) + 33554432) >> 18);
			dst[12 * u + 1] = (Byte)((r0 * 19589 + g0 * 38443 + b0 * 7502) >> 16);
			dst[12 * u + 2] = (Byte)(((b0 + b1 + b2 + b3) * (-5328) + (g0 + g1 + g2 + g3) * (-27438) + ((r0 + r1 + r2 + r3) << 15) + 33554432) >> 18);
			dst[12 * u + 3] = (Byte)((r1 * 19589 + g1 * 38443 + b1 * 7502) >> 16);
			dst[12 * u + 4] = (Byte)(((r4 + r5 + r6 + r7) * (-11058) + (g4 + g5 + g6 + g7) * (-21709) + ((b4 + b5 + b6 + b7) << 15) + 33554432) >> 18);
			dst[12 * u + 5] = (Byte)((r2 * 19589 + g2 * 38443 + b2 * 7502) >> 16);
			dst[12 * u + 6] = (Byte)(((b4 + b5 + b6 + b7) * (-5328) + (g4 + g5 + g6 + g7) * (-27438) + ((r4 + r5 + r6 + r7) << 15) + 33554432) >> 18);
			dst[12 * u + 7] = (Byte)((r3 * 19589 + g3 * 38443 + b3 * 7502) >> 16);
			dst[12 * u + 8] = (Byte)((r4 * 19589 + g4 * 38443 + b4 * 7502) >> 16);
			dst[12 * u + 9] = (Byte)((r5 * 19589 + g5 * 38443 + b5 * 7502) >> 16);
			dst[12 * u + 10] = (Byte)((r6 * 19589 + g6 * 38443 + b6 * 7502) >> 16);
			dst[12 * u + 11] = (Byte)((r7 * 19589 + g7 * 38443 + b7 * 7502) >> 16);
		}
	}

	return TRUE;
}

Bool videonext::bgrToYuv4p(CompatibleImage input, Binary& output)
{
	if (input.getType() != CompatibleImageType::ImagePointerColor && input.getType() != CompatibleImageType::IplImageColor) return FALSE;

	UInt width = input.width();
	UInt height = input.height();
	CompatibleChannelData channel = input.channels()[0];

	UInt outputSize = height * width * 3 / 2;
	if (output.size() != outputSize) output = Binary(outputSize);

	Byte *srcData = (Byte*)channel.data();
	UInt halfWidth = width / 2;
	UInt halfHeight = height / 2;

	for (UInt v = 0; v < halfHeight; v++)
	{
		Byte *src1 = &srcData[(2 * v) * channel.step()];
		Byte *src2 = &srcData[(2 * v + 1) * channel.step()];
		Byte *dst = &output[v * width * 3];

		for (UInt u = 0; u < halfWidth; u++)
		{
			Int b00 = src1[6 * u];
			Int g00 = src1[6 * u + 1];
			Int r00 = src1[6 * u + 2];
			Int b01 = src1[6 * u + 3];
			Int g01 = src1[6 * u + 4];
			Int r01 = src1[6 * u + 5];
			Int b10 = src2[6 * u];
			Int g10 = src2[6 * u + 1];
			Int r10 = src2[6 * u + 2];
			Int b11 = src2[6 * u + 3];
			Int g11 = src2[6 * u + 4];
			Int r11 = src2[6 * u + 5];

			dst[6 * u] = ((Byte)(((r00 + r01 + r10 + r11) * (-11058) + (g00 + g01 + g10 + g11) * (-21709) + ((b00 + b01 + b10 + b11) << 15) + 33554432) >> 18)) ^ 0x80;
			dst[6 * u + 1] = ((Byte)(((b00 + b01 + b10 + b11) * (-5328) + (g00 + g01 + g10 + g11) * (-27438) + ((r00 + r01 + r10 + r11) << 15) + 33554432) >> 18)) ^ 0x80;
			dst[6 * u + 2] = (Byte)((r00 * 19589 + g00 * 38443 + b00 * 7502) >> 16);
			dst[6 * u + 3] = (Byte)((r01 * 19589 + g01 * 38443 + b01 * 7502) >> 16);
			dst[6 * u + 4] = (Byte)((r10 * 19589 + g10 * 38443 + b10 * 7502) >> 16);
			dst[6 * u + 5] = (Byte)((r11 * 19589 + g11 * 38443 + b11 * 7502) >> 16);
		}
	}

	return TRUE;
}

Bool videonext::nv12ToYuv420(CompatibleImage input, CompatibleImage& output)
{
	if (input.getType() != CompatibleImageType::AVFrameNV12) return FALSE;

	UInt width = input.width();
	UInt height = input.height();
	if (output.width() != width || output.height() != height || output.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		output = CompatibleImage(CompatibleImageType::AVFrameYUV420P, input.size());
	}

	Array<CompatibleChannelData> srcChannels = input.channels();
	CompatibleChannelData srcYChannel = srcChannels[0];
	CompatibleChannelData srcUVChannel = srcChannels[1];

	Array<CompatibleChannelData> dstChannels = output.channels();
	CompatibleChannelData dstYChannel = dstChannels[0];
	CompatibleChannelData dstUChannel = dstChannels[1];
	CompatibleChannelData dstVChannel = dstChannels[2];

	if (srcYChannel.step() == dstYChannel.step())
	{
		utility::memoryCopy(srcYChannel.data(), dstYChannel.data(), srcYChannel.height() * srcYChannel.step());
	}
	else
	{
		for (UInt v = 0; v < srcYChannel.height(); v++)
		{
			utility::memoryCopy(&srcYChannel.data()[v * srcYChannel.step()], &dstYChannel.data()[v * dstYChannel.step()], srcYChannel.width());
		}
	}

	UInt uvStep = srcUVChannel.step(), uvHeight = srcUVChannel.height(), uvWidthBlock = srcUVChannel.width() / 4;
	UInt dstUStep = dstUChannel.step(), dstVStep = dstVChannel.step();
	for (UInt v = 0; v < uvHeight; v++)
	{
		UInt *srcUVRow = (UInt*)&srcUVChannel.data()[v * uvStep];
		UInt *dstURow = (UInt*)&dstUChannel.data()[v * dstUStep];
		UInt *dstVRow = (UInt*)&dstVChannel.data()[v * dstVStep];
		for (UInt u = 0; u < uvWidthBlock; u++)
		{
			UInt srcB1 = srcUVRow[2 * u];
			UInt srcB2 = srcUVRow[2 * u + 1];
			dstURow[u] = (srcB1 & 0x000000ff) | ((srcB1 & 0x00ff0000) >> 8) | ((srcB2 & 0x000000ff) << 16) | ((srcB2 & 0x00ff0000) << 8);
			dstVRow[u] = ((srcB1 & 0x0000ff00) >> 8) | ((srcB1 & 0xff000000) >> 16) | ((srcB2 & 0x0000ff00) << 8) | (srcB2 & 0xff000000);
		}
	}

	return TRUE;
}

Bool videonext::yuv420ToNv12(CompatibleImage input, CompatibleImage& output)
{
	if (input.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;

	UInt width = input.width();
	UInt height = input.height();
	if (output.width() != width || output.height() != height || output.getType() != CompatibleImageType::AVFrameNV12)
	{
		output = CompatibleImage(CompatibleImageType::AVFrameNV12, input.size());
	}

	Array<CompatibleChannelData> srcChannels = input.channels();
	CompatibleChannelData srcYChannel = srcChannels[0];
	CompatibleChannelData srcUChannel = srcChannels[1];
	CompatibleChannelData srcVChannel = srcChannels[2];

	Array<CompatibleChannelData> dstChannels = output.channels();
	CompatibleChannelData dstYChannel = dstChannels[0];
	CompatibleChannelData dstUVChannel = dstChannels[1];

	if (srcYChannel.step() == dstYChannel.step())
	{
		utility::memoryCopy(srcYChannel.data(), dstYChannel.data(), srcYChannel.height() * srcYChannel.step());
	}
	else
	{
		for (UInt v = 0; v < srcYChannel.height(); v++)
		{
			utility::memoryCopy(&srcYChannel.data()[v * srcYChannel.step()], &dstYChannel.data()[v * dstYChannel.step()], srcYChannel.width());
		}
	}

	UInt dstUVStep = dstUVChannel.step(), dstUVHeight = dstUVChannel.height(), dstUVWidthBlock = dstUVChannel.width() / 4;
	UInt srcUStep = srcUChannel.step(), srcVStep = srcVChannel.step();
	for (UInt v = 0; v < dstUVHeight; v++)
	{
		UInt *srcURow = (UInt*)&srcUChannel.data()[v * srcUStep];
		UInt *srcVRow = (UInt*)&srcVChannel.data()[v * srcVStep];
		UInt *dstUVRow = (UInt*)&dstUVChannel.data()[v * dstUVStep];
		for (UInt u = 0; u < dstUVWidthBlock; u++)
		{
			UInt srcU = srcURow[u], srcV = srcVRow[u];
			dstUVRow[2 * u] = (srcU & 0x000000ff) | ((srcV & 0x000000ff) << 8) | ((srcU & 0x0000ff00) << 8) | ((srcV & 0x0000ff00) << 16);
			dstUVRow[2 * u + 1] = ((srcU & 0x00ff0000) >> 16) | ((srcV & 0x00ff0000) >> 8) | ((srcU & 0xff000000) >> 8) | (srcV & 0xff000000);
		}
	}

	return TRUE;
}

Bool videonext::splitYuv420(CompatibleImage input, UInt forWidth, CompatibleImage& y, CompatibleImage& u, CompatibleImage& v)
{
	if (input.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;

	StructImage srcY, srcU, srcV;
	Array<CompatibleChannelData> channels = input.channels();
	if (!convStructImage(channels[0], srcY) || !convStructImage(channels[1], srcU) || !convStructImage(channels[2], srcV)) return FALSE;

	Bool canThird = isDividable(input.size(), 3);
	Bool canQuarter = isDividable(input.size(), 4);
	Float ratio = (Float)input.width() / forWidth;

	if (ratio >= 4 && canQuarter)
	{
		StructImage dstY = renewImagePointer(y, input.width() / 4, input.height() / 4, 0);
		byteGrayQuarter(&srcY, &dstY, 0, 0);
	}
	else if (ratio >= 3 && canThird)
	{
		StructImage dstY = renewImagePointer(y, input.width() / 3, input.height() / 3, 0);
		byteGrayThird(&srcY, &dstY, 0, 0);
	}
	else if (ratio >= 2)
	{
		StructImage dstY = renewImagePointer(y, input.width() / 2, input.height() / 2, 0);
		byteGrayHalf(&srcY, &dstY, 0, 0);
	}
	else
	{
		y = CompatibleImage(channels[0]);
	}

	if (ratio >= 4 && canQuarter)
	{
		StructImage dstU = renewImagePointer(u, input.width() / 4, input.height() / 4, 128);
		StructImage dstV = renewImagePointer(v, input.width() / 4, input.height() / 4, 128);
		byteGrayHalf(&srcU, &dstU, 0, 0);
		byteGrayHalf(&srcV, &dstV, 0, 0);
	}
	else
	{
		u = CompatibleImage(channels[1]);
		v = CompatibleImage(channels[2]);
	}

	return TRUE;
}

Bool videonext::splitYuv420ToSameSize(CompatibleImage input, UInt resizeRatio/* 1, 2, 3, 4, 6 */, CompatibleImage& y, CompatibleImage& u, CompatibleImage& v)
{
	if (input.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;

	StructImage srcY, srcU, srcV;
	Array<CompatibleChannelData> channels = input.channels();
	if (!convStructImage(channels[0], srcY) || !convStructImage(channels[1], srcU) || !convStructImage(channels[2], srcV)) return FALSE;

	if (resizeRatio == 6)
	{
		if (!isDividable(input.size(), 6)) return FALSE;

		StructImage dstY = renewImagePointer(y, input.width() / 6, input.height() / 6, 0);
		byteGraySixth(&srcY, &dstY, 0, 0);

		StructImage dstU = renewImagePointer(u, input.width() / 6, input.height() / 6, 128);
		StructImage dstV = renewImagePointer(v, input.width() / 6, input.height() / 6, 128);
		byteGrayThird(&srcU, &dstU, 0, 0);
		byteGrayThird(&srcV, &dstV, 0, 0);
	}
	else if (resizeRatio == 4)
	{
		if (!isDividable(input.size(), 4)) return FALSE;

		StructImage dstY = renewImagePointer(y, input.width() / 4, input.height() / 4, 0);
		byteGrayQuarter(&srcY, &dstY, 0, 0);

		StructImage dstU = renewImagePointer(u, input.width() / 4, input.height() / 4, 128);
		StructImage dstV = renewImagePointer(v, input.width() / 4, input.height() / 4, 128);
		byteGrayHalf(&srcU, &dstU, 0, 0);
		byteGrayHalf(&srcV, &dstV, 0, 0);
	}
	else if (resizeRatio == 3)
	{
		if (!isDividable(input.size(), 3)) return FALSE;

		StructImage dstY = renewImagePointer(y, input.width() / 3, input.height() / 3, 0);
		byteGrayThird(&srcY, &dstY, 0, 0);

		StructImage dstU = renewImagePointer(u, input.width() / 3, input.height() / 3, 128);
		StructImage dstV = renewImagePointer(v, input.width() / 3, input.height() / 3, 128);
		byteGrayDoubleThird(&srcU, &dstU, 0, 0);
		byteGrayDoubleThird(&srcV, &dstV, 0, 0);
	}
	else if (resizeRatio == 2)
	{
		StructImage dstY = renewImagePointer(y, input.width() / 2, input.height() / 2, 0);
		byteGrayHalf(&srcY, &dstY, 0, 0);

		u = CompatibleImage(channels[1]);
		v = CompatibleImage(channels[2]);
	}
	else if (resizeRatio == 1)
	{
		y = CompatibleImage(channels[0]);

		StructImage dstU = renewImagePointer(u, input.width(), input.height(), 128);
		StructImage dstV = renewImagePointer(v, input.width(), input.height(), 128);
		byteGrayDouble(&srcU, &dstU, 0, 0);
		byteGrayDouble(&srcV, &dstV, 0, 0);
	}
	else return FALSE;

	return TRUE;
}

Bool videonext::splitYuv420ToSameSizeSegment(CompatibleImage input, UInt resizeRatio/* 1, 2, 4 */, Region2D segment, CompatibleImage& y, CompatibleImage& u, CompatibleImage& v)
{
	if (input.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;

	StructImage srcY, srcU, srcV;
	Array<CompatibleChannelData> channels = input.channels();
	if (!convStructImage(channels[0], srcY) || !convStructImage(channels[1], srcU) || !convStructImage(channels[2], srcV)) return FALSE;

	StructImage dstY = renewImagePointer(y, segment.width, segment.height, 0);
	StructImage dstU = renewImagePointer(u, segment.width, segment.height, 128);
	StructImage dstV = renewImagePointer(v, segment.width, segment.height, 128);

	if (resizeRatio == 4)
	{
		if (!isDividable(input.size(), 4)) return FALSE;

		byteGrayQuarter(&srcY, &dstY, segment.offsetU * 4, segment.offsetV * 4);
		byteGrayHalf(&srcU, &dstU, segment.offsetU * 2, segment.offsetV * 2);
		byteGrayHalf(&srcV, &dstV, segment.offsetU * 2, segment.offsetV * 2);
	}
	else if (resizeRatio == 2)
	{
		byteGrayHalf(&srcY, &dstY, segment.offsetU * 2, segment.offsetV * 2);
		byteGrayCopy(&srcU, &dstU, segment.offsetU, segment.offsetV);
		byteGrayCopy(&srcV, &dstV, segment.offsetU, segment.offsetV);
	}
	else if (resizeRatio == 1)
	{
		byteGrayCopy(&srcY, &dstY, segment.offsetU, segment.offsetV);
		byteGrayDouble(&srcU, &dstU, segment.offsetU / 2, segment.offsetV / 2);
		byteGrayDouble(&srcV, &dstV, segment.offsetU / 2, segment.offsetV / 2);
	}
	else return FALSE;

	return TRUE;
}

Bool videonext::splitNv12(CompatibleImage input, UInt forWidth, CompatibleImage& y, CompatibleImage& u, CompatibleImage& v)
{
	if (input.getType() != CompatibleImageType::AVFrameNV12) return FALSE;

	StructImage srcY, srcUV;
	Array<CompatibleChannelData> channels = input.channels();
	if (!convStructImage(channels[0], srcY) || !convStructImage(channels[1], srcUV)) return FALSE;

	Bool canThird = isDividable(input.size(), 3);
	Bool canQuarter = isDividable(input.size(), 4);
	Float ratio = (Float)input.width() / forWidth;

	if (ratio >= 4 && canQuarter)
	{
		StructImage dstY = renewImagePointer(y, input.width() / 4, input.height() / 4, 0);
		byteGrayQuarter(&srcY, &dstY, 0, 0);
	}
	else if (ratio >= 3 && canThird)
	{
		StructImage dstY = renewImagePointer(y, input.width() / 3, input.height() / 3, 0);
		byteGrayThird(&srcY, &dstY, 0, 0);
	}
	else if (ratio >= 2)
	{
		StructImage dstY = renewImagePointer(y, input.width() / 2, input.height() / 2, 0);
		byteGrayHalf(&srcY, &dstY, 0, 0);
	}
	else
	{
		y = CompatibleImage(channels[0]);
	}

	if (ratio >= 4 && canQuarter)
	{
		StructImage dstU = renewImagePointer(u, input.width() / 4, input.height() / 4, 128);
		StructImage dstV = renewImagePointer(v, input.width() / 4, input.height() / 4, 128);
		nv12Half(&srcUV, &dstU, &dstV);
	}
	else
	{
		StructImage dstU = renewImagePointer(u, input.width() / 2, input.height() / 2, 128);
		StructImage dstV = renewImagePointer(v, input.width() / 2, input.height() / 2, 128);
		nv12Copy(&srcUV, &dstU, &dstV);
	}

	return TRUE;
}

Bool videonext::splitYuv411(Binary input, Size2D inputSize, UInt forWidth, CompatibleImage& y, CompatibleImage& u, CompatibleImage& v)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 3 / 2) return FALSE;

	Float ratio = (Float)inputSize.width / forWidth;

	if (ratio >= 4)
	{
		StructImage dstY = renewImagePointer(y, inputSize.width / 4, inputSize.height / 4, 0);
		StructImage dstU = renewImagePointer(u, inputSize.width / 4, inputSize.height / 4, 128);
		StructImage dstV = renewImagePointer(v, inputSize.width / 4, inputSize.height / 4, 128);
		yuv411Quarter(input, inputSize, &dstY, &dstU, &dstV);
	}
	else if (ratio >= 2)
	{
		StructImage dstY = renewImagePointer(y, inputSize.width / 2, inputSize.height / 2, 0);
		StructImage dstU = renewImagePointer(u, inputSize.width / 4, inputSize.height / 2, 128);
		StructImage dstV = renewImagePointer(v, inputSize.width / 4, inputSize.height / 2, 128);
		yuv411Half(input, inputSize, &dstY, &dstU, &dstV);
	}
	else
	{
		StructImage dstY = renewImagePointer(y, inputSize.width, inputSize.height, 0);
		StructImage dstU = renewImagePointer(u, inputSize.width / 4, inputSize.height, 128);
		StructImage dstV = renewImagePointer(v, inputSize.width / 4, inputSize.height, 128);
		yuv411Copy(input, inputSize, &dstY, &dstU, &dstV);
	}

	return TRUE;
}

Bool videonext::splitYuv411ToSameSize(Binary input, Size2D inputSize, UInt resizeRatio/* 1, 2, 4 */, CompatibleImage& y, CompatibleImage& u, CompatibleImage& v)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 3 / 2) return FALSE;

	if (resizeRatio == 4)
	{
		StructImage dstY = renewImagePointer(y, inputSize.width / 4, inputSize.height / 4, 0);
		StructImage dstU = renewImagePointer(u, inputSize.width / 4, inputSize.height / 4, 128);
		StructImage dstV = renewImagePointer(v, inputSize.width / 4, inputSize.height / 4, 128);
		yuv411Quarter(input, inputSize, &dstY, &dstU, &dstV);
	}
	else if (resizeRatio == 2)
	{
		StructImage dstY = renewImagePointer(y, inputSize.width / 2, inputSize.height / 2, 0);
		StructImage dstU = renewImagePointer(u, inputSize.width / 2, inputSize.height / 2, 128);
		StructImage dstV = renewImagePointer(v, inputSize.width / 2, inputSize.height / 2, 128);
		yuv411HalfSameSize(input, inputSize, &dstY, &dstU, &dstV);
	}
	else if (resizeRatio == 1)
	{
		StructImage dstY = renewImagePointer(y, inputSize.width, inputSize.height, 0);
		StructImage dstU = renewImagePointer(u, inputSize.width, inputSize.height, 128);
		StructImage dstV = renewImagePointer(v, inputSize.width, inputSize.height, 128);
		yuv411CopySameSize(input, inputSize, &dstY, &dstU, &dstV);
	}
	else return FALSE;

	return TRUE;
}

Bool videonext::yuvToBgrFine(CompatibleImage yImage, CompatibleImage uImage, CompatibleImage vImage, CompatibleImage& output)
{
	if (yImage.getType() != CompatibleImageType::ImagePointerGray && yImage.getType() != CompatibleImageType::IplImageGray && yImage.getType() != CompatibleImageType::CompatibleChannelGray) return FALSE;
	if (uImage.getType() != CompatibleImageType::ImagePointerGray && uImage.getType() != CompatibleImageType::IplImageGray && uImage.getType() != CompatibleImageType::CompatibleChannelGray) return FALSE;
	if (vImage.getType() != CompatibleImageType::ImagePointerGray && vImage.getType() != CompatibleImageType::IplImageGray && vImage.getType() != CompatibleImageType::CompatibleChannelGray) return FALSE;
	if (yImage.width() != uImage.width() || yImage.width() != vImage.width()) return FALSE;
	if (yImage.height() != uImage.height() || yImage.height() != vImage.height()) return FALSE;

	UInt width = yImage.width();
	UInt height = yImage.height();

	if (output.getType() != CompatibleImageType::ImagePointerColor || output.width() != width || output.height() != height)
	{
		ImagePointer newImagePtr(Size2D::wh(width, height), TRUE);
		output = CompatibleImage(newImagePtr);
	}

	CompatibleChannelData yChannel = yImage.channels()[0];
	CompatibleChannelData uChannel = uImage.channels()[0];
	CompatibleChannelData vChannel = vImage.channels()[0];
	CompatibleChannelData outputChannel = output.channels()[0];

	for (UInt v = 0; v < height; v++)
	{
		Byte *yRow = &yChannel.data()[v * yChannel.step()];
		Byte *uRow = &uChannel.data()[v * uChannel.step()];
		Byte *vRow = &vChannel.data()[v * vChannel.step()];
		Byte *dstRow = &outputChannel.data()[v * outputChannel.step()];

		for (UInt u = 0; u < width; u++)
		{
			Byte yVal = yRow[u];
			Byte uVal = uRow[u];
			Byte vVal = vRow[u];
			dstRow[3 * u] = (Byte)(math::clamp((Int)yVal * 65536 + (Int)uVal * 116129 - 14864512, 0, 16711680) >> 16);
			dstRow[3 * u + 1] = (Byte)(math::clamp((Int)yVal * 65536 - (Int)uVal * 22553 - (Int)vVal * 46801 + 8877312, 0, 16711680) >> 16);
			dstRow[3 * u + 2] = (Byte)(math::clamp((Int)yVal * 65536 + (Int)vVal * 91881 - 11760768, 0, 16711680) >> 16);
		}
	}

	return TRUE;
}

Bool videonext::yuvToBgrCoarse(CompatibleImage yImage, CompatibleImage uImage, CompatibleImage vImage, CompatibleImage& output)
{
	if (yImage.getType() != CompatibleImageType::ImagePointerGray && yImage.getType() != CompatibleImageType::IplImageGray && yImage.getType() != CompatibleImageType::CompatibleChannelGray) return FALSE;
	if (uImage.getType() != CompatibleImageType::ImagePointerGray && uImage.getType() != CompatibleImageType::IplImageGray && uImage.getType() != CompatibleImageType::CompatibleChannelGray) return FALSE;
	if (vImage.getType() != CompatibleImageType::ImagePointerGray && vImage.getType() != CompatibleImageType::IplImageGray && vImage.getType() != CompatibleImageType::CompatibleChannelGray) return FALSE;
	if (yImage.width() != uImage.width() || yImage.width() != vImage.width()) return FALSE;
	if (yImage.height() != uImage.height() || yImage.height() != vImage.height()) return FALSE;

	UInt width = yImage.width();
	UInt height = yImage.height();

	if (output.getType() != CompatibleImageType::ImagePointerColor || output.width() != width || output.height() != height)
	{
		ImagePointer newImagePtr(Size2D::wh(width, height), TRUE);
		output = CompatibleImage(newImagePtr);
	}

	CompatibleChannelData yChannel = yImage.channels()[0];
	CompatibleChannelData uChannel = uImage.channels()[0];
	CompatibleChannelData vChannel = vImage.channels()[0];
	CompatibleChannelData outputChannel = output.channels()[0];

	Array<UInt> table = getTableYuv2Bgr();
	UInt *tableData = table.data();

	UInt nBlocks = width / 4;
	for (UInt v = 0; v < height; v++)
	{
		UInt *yRowUInt = (UInt*)&yChannel.data()[v * yChannel.step()];
		UInt *uRowUInt = (UInt*)&uChannel.data()[v * uChannel.step()];
		UInt *vRowUInt = (UInt*)&vChannel.data()[v * vChannel.step()];
		UInt *dstRowUInt = (UInt*)&outputChannel.data()[v * outputChannel.step()];
		for (UInt b = 0; b < nBlocks; b++)
		{
			UInt yVals = yRowUInt[b];
			UInt uVals = uRowUInt[b];
			UInt vVals = vRowUInt[b];
			UInt bgr1 = tableData[((yVals & 0x000000fe) >> 1) | ((uVals & 0x000000fe) << 6) | ((vVals & 0x000000fe) << 13)]; // COARSE_SCALE=128
			UInt bgr2 = tableData[((yVals & 0x0000fe00) >> 9) | ((uVals & 0x0000fe00) >> 2) | ((vVals & 0x0000fe00) << 5)]; // COARSE_SCALE=128
			UInt bgr3 = tableData[((yVals & 0x00fe0000) >> 17) | ((uVals & 0x00fe0000) >> 10) | ((vVals & 0x00fe0000) >> 3)]; // COARSE_SCALE=128
			UInt bgr4 = tableData[((yVals & 0xfe000000) >> 25) | ((uVals & 0xfe000000) >> 18) | ((vVals & 0xfe000000) >> 11)]; // COARSE_SCALE=128
			// UInt bgr1 = tableData[((yVals & 0x000000fc) >> 2) | ((uVals & 0x000000fc) << 4) | ((vVals & 0x000000fc) << 10)]; // COARSE_SCALE=64
			// UInt bgr2 = tableData[((yVals & 0x0000fc00) >> 10) | ((uVals & 0x0000fc00) >> 4) | ((vVals & 0x0000fc00) << 2)]; // COARSE_SCALE=64
			// UInt bgr3 = tableData[((yVals & 0x00fc0000) >> 18) | ((uVals & 0x00fc0000) >> 12) | ((vVals & 0x00fc0000) >> 6)]; // COARSE_SCALE=64
			// UInt bgr4 = tableData[((yVals & 0xfc000000) >> 26) | ((uVals & 0xfc000000) >> 20) | ((vVals & 0xfc000000) >> 14)]; // COARSE_SCALE=64
			dstRowUInt[3 * b] = bgr1 | ((bgr2 & 0x000000ff) << 24);
			dstRowUInt[3 * b + 1] = ((bgr2 & 0x00ffff00) >> 8) | ((bgr3 & 0x0000ffff) << 16);
			dstRowUInt[3 * b + 2] = ((bgr3 & 0x00ff0000) >> 16) | (bgr4 << 8);
		}

		Byte *yRow = (Byte*)yRowUInt;
		Byte *uRow = (Byte*)uRowUInt;
		Byte *vRow = (Byte*)vRowUInt;
		Byte *dstRow = (Byte*)dstRowUInt;
		for (UInt u = nBlocks * 4; u < width; u++)
		{
			Byte yVal = yRow[u];
			Byte uVal = uRow[u];
			Byte vVal = vRow[u];
			UInt bgr = tableData[((UInt)(yVal & 0xfe) >> 1) | ((UInt)(uVal & 0xfe) << 6) | ((UInt)(vVal & 0xfe) << 13)]; // COARSE_SCALE=128
			// UInt bgr = tableData[((UInt)(yVal & 0xfc) >> 2) | ((UInt)(uVal & 0xfc) << 4) | ((UInt)(vVal & 0xfc) << 10)]; // COARSE_SCALE=64
			dstRow[3 * u] = (Byte)(bgr & 0x000000ff);
			dstRow[3 * u + 1] = (Byte)((bgr & 0x0000ff00) >> 8);
			dstRow[3 * u + 2] = (Byte)((bgr & 0x00ff0000) >> 16);
		}
	}

	return TRUE;
}

Bool videonext::resizeYuv420Half(CompatibleImage src, CompatibleImage& dst)
{
	if (src.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;
	if (!isDividable(src.size(), 2)) return FALSE;

	if (dst.getType() != CompatibleImageType::AVFrameYUV420P || dst.width() != src.width() / 2 || dst.height() != src.height() / 2)
	{
		Pointer frame = av_frame_alloc();
		if (!frame) return FALSE;

		av_frame_set_format(frame, AVPixelFormat::AV_PIX_FMT_YUV420P);
		av_frame_set_width(frame, src.width() / 2);
		av_frame_set_height(frame, src.height() / 2);

		Int resultCreateFrameData = av_frame_get_buffer(frame, 0);
		if (resultCreateFrameData < 0) return FALSE;

		dst = CompatibleImage(frame, TRUE);
	}

	Array<CompatibleChannelData> srcChannels = src.channels();
	Array<CompatibleChannelData> dstChannels = dst.channels();
	for (UInt i = 0; i < 3; i++)
	{
		resizeChannelHalf(srcChannels[i], dstChannels[i]);
	}

	return TRUE;
}

Bool videonext::resizeYuv420OneThird(CompatibleImage src, CompatibleImage& dst)
{
	if (src.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;
	if (!isDividable(src.size(), 3)) return FALSE;

	if (dst.getType() != CompatibleImageType::AVFrameYUV420P || dst.width() != src.width() / 3 || dst.height() != src.height() / 3)
	{
		Pointer frame = av_frame_alloc();
		if (!frame) return FALSE;

		av_frame_set_format(frame, AVPixelFormat::AV_PIX_FMT_YUV420P);
		av_frame_set_width(frame, src.width() / 3);
		av_frame_set_height(frame, src.height() / 3);

		Int resultCreateFrameData = av_frame_get_buffer(frame, 0);
		if (resultCreateFrameData < 0) return FALSE;

		dst = CompatibleImage(frame, TRUE);
	}

	Array<CompatibleChannelData> srcChannels = src.channels();
	Array<CompatibleChannelData> dstChannels = dst.channels();
	for (UInt i = 0; i < 3; i++)
	{
		resizeChannelOneThird(srcChannels[i], dstChannels[i]);
	}

	return TRUE;
}

Bool videonext::resizeYuv420Quarter(CompatibleImage src, CompatibleImage& dst)
{
	if (src.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;
	if (!isDividable(src.size(), 4)) return FALSE;

	if (dst.getType() != CompatibleImageType::AVFrameYUV420P || dst.width() != src.width() / 4 || dst.height() != src.height() / 4)
	{
		Pointer frame = av_frame_alloc();
		if (!frame) return FALSE;

		av_frame_set_format(frame, AVPixelFormat::AV_PIX_FMT_YUV420P);
		av_frame_set_width(frame, src.width() / 4);
		av_frame_set_height(frame, src.height() / 4);

		Int resultCreateFrameData = av_frame_get_buffer(frame, 0);
		if (resultCreateFrameData < 0) return FALSE;

		dst = CompatibleImage(frame, TRUE);
	}

	Array<CompatibleChannelData> srcChannels = src.channels();
	Array<CompatibleChannelData> dstChannels = dst.channels();
	for (UInt i = 0; i < 3; i++)
	{
		resizeChannelQuarter(srcChannels[i], dstChannels[i]);
	}

	return TRUE;
}

Bool videonext::resizeYuv420OneSixth(CompatibleImage src, CompatibleImage& dst)
{
	if (src.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;
	if (!isDividable(src.size(), 6)) return FALSE;

	if (dst.getType() != CompatibleImageType::AVFrameYUV420P || dst.width() != src.width() / 6 || dst.height() != src.height() / 6)
	{
		Pointer frame = av_frame_alloc();
		if (!frame) return FALSE;

		av_frame_set_format(frame, AVPixelFormat::AV_PIX_FMT_YUV420P);
		av_frame_set_width(frame, src.width() / 6);
		av_frame_set_height(frame, src.height() / 6);

		Int resultCreateFrameData = av_frame_get_buffer(frame, 0);
		if (resultCreateFrameData < 0) return FALSE;

		dst = CompatibleImage(frame, TRUE);
	}

	Array<CompatibleChannelData> srcChannels = src.channels();
	Array<CompatibleChannelData> dstChannels = dst.channels();
	for (UInt i = 0; i < 3; i++)
	{
		resizeChannelOneSixth(srcChannels[i], dstChannels[i]);
	}

	return TRUE;
}

Bool videonext::resizeYuv420OneEighth(CompatibleImage src, CompatibleImage& dst, Bool clipToDividable)
{
	if (src.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;
	if (!clipToDividable && !isDividable(src.size(), 8)) return FALSE;

	if (dst.getType() != CompatibleImageType::AVFrameYUV420P || dst.width() != src.width() / 8 || dst.height() != src.height() / 8)
	{
		Pointer frame = av_frame_alloc();
		if (!frame) return FALSE;

		av_frame_set_format(frame, AVPixelFormat::AV_PIX_FMT_YUV420P);
		av_frame_set_width(frame, src.width() / 8);
		av_frame_set_height(frame, src.height() / 8);

		Int resultCreateFrameData = av_frame_get_buffer(frame, 0);
		if (resultCreateFrameData < 0) return FALSE;

		dst = CompatibleImage(frame, TRUE);
	}

	Array<CompatibleChannelData> srcChannels = src.channels();
	Array<CompatibleChannelData> dstChannels = dst.channels();
	for (UInt i = 0; i < 3; i++)
	{
		resizeChannelOneEighth(srcChannels[i], dstChannels[i]);
	}

	return TRUE;
}

Bool videonext::resizeOnlyYHalf(CompatibleImage src, CompatibleImage& dst)
{
	if (src.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;
	if (!isDividable(src.size(), 2)) return FALSE;

	if (dst.getType() != CompatibleImageType::ImagePointerGray || dst.width() != src.width() / 2 || dst.height() != src.height() / 2)
	{
		dst = CompatibleImage(CompatibleImageType::ImagePointerGray, Size2D::wh(src.width() / 2, src.height() / 2));
	}

	Array<CompatibleChannelData> srcChannels = src.channels();
	Array<CompatibleChannelData> dstChannels = dst.channels();
	resizeChannelHalf(srcChannels[0], dstChannels[0]);

	return TRUE;
}

Bool videonext::yuv411ToYuv420(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 3 / 2) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		dst = CompatibleImage(CompatibleImageType::AVFrameYUV420P, inputSize);
	}

	Array<CompatibleChannelData> channels = dst.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	yuv411ToYuv420(input, inputSize, &y, &u, &v);

	return TRUE;
}

Bool videonext::yuv4pToYuv420(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 3 / 2) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		dst = CompatibleImage(CompatibleImageType::AVFrameYUV420P, inputSize);
	}

	Array<CompatibleChannelData> channels = dst.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	yuv4pToYuv420(input, inputSize, &y, &u, &v);

	return TRUE;
}

Bool videonext::yuv420ToYuv4p(CompatibleImage input, Binary& output)
{
	if (input.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;
	if (!isDividable(input.size(), 2)) return FALSE;

	UInt outputSize = input.width() * input.height() * 3 / 2;
	if (output.size() != outputSize) output = Binary(outputSize);

	Array<CompatibleChannelData> channels = input.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	yuv420ToYuv4p(&y, &u, &v, output.data());

	return TRUE;
}

Bool videonext::yuv422ToYuv420(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 2) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		dst = CompatibleImage(CompatibleImageType::AVFrameYUV420P, inputSize);
	}

	Array<CompatibleChannelData> channels = dst.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	yuv422ToYuv420(input, inputSize, &y, &u, &v);

	return TRUE;
}

Bool videonext::raw8ToYuv420(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		dst = CompatibleImage(CompatibleImageType::AVFrameYUV420P, inputSize);
	}

	Array<CompatibleChannelData> channels = dst.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	raw8ToYuv420(input, inputSize, &y, &u, &v);

	return TRUE;
}

Bool videonext::raw12ToYuv420(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 3 / 2) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		dst = CompatibleImage(CompatibleImageType::AVFrameYUV420P, inputSize);
	}

	Array<CompatibleChannelData> channels = dst.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	raw12ToYuv420(input, inputSize, &y, &u, &v);

	return TRUE;
}

Bool videonext::raw14ToYuv420(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 7 / 4) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		dst = CompatibleImage(CompatibleImageType::AVFrameYUV420P, inputSize);
	}

	Array<CompatibleChannelData> channels = dst.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	raw14ToYuv420(input, inputSize, &y, &u, &v);

	return TRUE;
}

Bool videonext::raw16ToYuv420(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 2) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		dst = CompatibleImage(CompatibleImageType::AVFrameYUV420P, inputSize);
	}

	Array<CompatibleChannelData> channels = dst.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	raw16ToYuv420(input, inputSize, &y, &u, &v);

	return TRUE;
}

Bool videonext::y16ToYuv420(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height * 2) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::AVFrameYUV420P)
	{
		dst = CompatibleImage(CompatibleImageType::AVFrameYUV420P, inputSize);
	}

	Array<CompatibleChannelData> channels = dst.channels();
	StructImage y, u, v;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v)) return FALSE;

	y16ToYuv420(input, inputSize, &y, &u, &v);

	return TRUE;
}

Bool videonext::raw8ToBgr(Binary input, Size2D inputSize, CompatibleImage& dst)
{
	if (inputSize.width < 8 || inputSize.height < 8 || input.size() != inputSize.width * inputSize.height) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::ImagePointerColor)
	{
		dst = CompatibleImage(CompatibleImageType::ImagePointerColor, inputSize);
	}

	CompatibleChannelData bgrChannel = dst.channels()[0];
	StructImage bgr;
	if (!convStructImage(bgrChannel, bgr)) return FALSE;

	raw8ToBgr(input, inputSize, &bgr);

	return TRUE;
}

Bool videonext::yuv420ToBgr(CompatibleImage input, CompatibleImage& dst)
{
	if (input.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;

	Size2D inputSize = input.size();
	if (!isDividable(inputSize, 2)) return FALSE;

	if (dst.width() != inputSize.width || dst.height() != inputSize.height || dst.getType() != CompatibleImageType::ImagePointerColor)
	{
		dst = CompatibleImage(CompatibleImageType::ImagePointerColor, inputSize);
	}

	Array<CompatibleChannelData> channels = input.channels();
	CompatibleChannelData bgrChannel = dst.channels()[0];
	StructImage y, u, v, bgr;
	if (!convStructImage(channels[0], y) || !convStructImage(channels[1], u) || !convStructImage(channels[2], v) || !convStructImage(bgrChannel, bgr)) return FALSE;

	yuv420ToBgr(&y, &u, &v, &bgr);

	return TRUE;
}